merge PR #158 and fix bug with garbled code samples

This commit is contained in:
mDuo13
2016-02-04 16:45:24 -08:00
34 changed files with 6691 additions and 9707 deletions

View File

@@ -1,241 +0,0 @@
Demurrage in Ripple Currencies
==============================
[Demurrage](http://en.wikipedia.org/wiki/Demurrage_%28currency%29) is a
negative interest rate on assets held that represents the cost of
holding those assets. Ripple supports currencies that have interest or
demurrage rates built into the currency definition, such as
"XAU (-0.5%pa)". Each currency-rate combination is treated as a completely
distinct currency for purposes of pathfinding and order books.
A gateway that wants to charge demurrage for holding assets (such as
gold) can issue a custom currency representing that asset with the
demurrage rate built-in. This is reflected in a different hex
representation for the demurraging currency. Client applications should
represent a demurraging currency by displaying the negative annual
percentage rate along with the currency code.
XRP cannot have demurrage or interest.
Dealing with Demurraging Currencies
-----------------------------------
The rippled server, and consequently the official global ledger, do not
track changes in value due to demurrage in the balance of funds. This
allows demurrage costs to be applied continuously, instead of operating
on fixed intervals. It also prevents demurrage from inflicting
significant additional computational and storage load for servers that
are part of the network. Instead, the fact that particular holdings are
subject to demurrage is tracked in the ledger by virtue of the custom
currency codes. Demurrage is calculated on the entire currency as if it
were interest, according to the following formula:
D = A \* ( e \^ (t/ τ) )
where:
- D is the amount after demurrage
- A is the pre-demurrage amount as recorded in the global ledger
- e is Euler's number
- t is the number of seconds since the [Ripple
Epoch](rippled-apis.html#specifying-time) (0:00 on January 1, 2000
UTC)
- τ is the [e-folding time](http://en.wikipedia.org/wiki/E-folding) in
seconds. This value is calculated from the desired interest rate.
You can think of demurrage in the Ripple Network as similar to
inflation, where the value of all assets affected by it decreases over
time, but the ledger always holds amounts in year-2000 values. (This
representation was chosen as a simplification of the more complicated
representation where individual holdings could track when the demurrage
on them started accruing, because this way it becomes easier to
recognize, exchange, and represent assets with demurrage applied.) Keep
in mind, this does not reflect actual, real-world inflation: instead,
it's hypothetical inflation at a constant rate.
### Calculating e-folding time
It is simple to calculate from a targeted demurrage rate in annual
percent to get a τ value to use in calculating demurrage:
1. First, subtract the demurrage percentage rate from 100% to get the
percentage of initial amount that remains after annual demurrage.
Represent it as a decimal. For example, for 0.5% annual interest,
you would get 0.995
2. Now, take the natural log of that number. For example, ln(0.995).
For traditional demurrage (decrease in value over time), this value
will be negative.
3. Finally, take the number of seconds in one year (31536000) and
divide by the result of the natural log operation. The result is
your e-folding time in seconds, for example -6291418827.045599
If you're curious: Since an e-folding amount represents how long until
an investment increases e times, a negative interest rate means that the
investment would have been worth e times its value that amount of time
in the past. Alternatively, it means that after that amount of seconds,
the investment will be worth 1/e of what it used to be.
### Canonical Calculations
For purposes of calculating demurrage consistently across applications,
the precision used is important. Our canonical source of demurrage
calculations is ripple-lib. By following these specifications, you
should be able to reproduce the demurrage results from ripple-lib
exactly:
First, recall the canonical formula for demurrage:
D = A \* ( e \^ (t/ τ) )
where D is the post-demurrage amount, and A is the pre-demurrage amount.
For the remainder of the formula, e \^ (t/ τ), we call this the
"demurrage coefficient". The demurrage coefficient is always relative to
a specific time, such that demurrage is calculated for the period
starting at the beginning of the Ripple Epoch (00:00:00 January 1, 2000)
The two directional calculations can therefore be simplified to:
1. Find the demurrage coefficient for the reference time
2. Apply it to the amount to convert
1. To convert ledger values to display values, multiply by the
demurrage coefficient
2. To convert display values to ledger values, divide by the
demurrage coefficient
3. Make sure that the converted value can be represented to the desired
accuracy. For example, ledger values submitted to Ripple must fit in
Ripple's [internal format](https://wiki.ripple.com/Currency_format).
For more information on the necessary precision:
The demurrage coefficient should be calculated entirely in [64-bit
IEEE754
doubles](http://en.wikipedia.org/wiki/Double-precision_floating-point_format),
such as the number types native to Javascript, or the float type
available in Python. However, there are some additional notes:
- For purposes of demurrage, one year is defined as exactly 31536000
seconds. This is exactly 365 days, with no adjustments for leap days
or leap seconds.
- The reference time should be specified in seconds, as an integer. If
your clock provides a higher resolution, you should truncate the
reference time to seconds before using it.
Client Applications
-------------------
In order to accurately convey amounts in present-day terms, client
applications must adjust for demurrage. This means a few things:
- When representing the value of a demurraging currency, the display
value should be adjusted to the "post-demurrage" values. (This
generally means that display values will be lower than the ledger
values.)
- When making offers or transactions in a demurraging currency,
amounts entered by the user should be adjusted upward, to interpret
user input as post-demurrage numbers. (This generally means that
amounts written into offers are higher than the user input value)
Client applications must also make sure to distinguish between
currencies with differing amounts of demurrage, and to display the
correct demurrage rates for all currencies with such rates. Currently,
the only Ripple Labs-created client that supports demurrage is Ripple
Trade.
### ripple-lib support
Clients that are built from
[ripple-lib](https://github.com/ripple/ripple-lib) can pass a
`reference_date` as an option to the Amount.from\_human function to
account for demurrage. This function can automatically convert a
human-input number to the necessary amount to hold on the ledger to
represent that value in at a given time. (The amount that is sent to the
rippled server is the hypothetical amount one would have needed in order
to have the desired amount after enduring constant demurrage since the
Ripple Epoch.)
For example, if you're using javascript, you can use ripple-lib utility
to calculate this manually:
```
// create an Amount object for an amount of the demurring currency, in this case 10
// pass in a reference_date that represents today,
// which will calculate how much you will need to represent the requested amount for today
var demAmount = Amount.from_human('10 0158415500000000C1F76FF6ECB0BAC600000000', {reference_date:459990264});
// set the issuer -- optional
demAmount.set_issuer("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
// get the json format that can be used as TakerPays or TakerGets in the order creation
console.log(demAmount.to_json());
// this will output an Amount with ~10.75 as the value for XAU, which is what is needed to create an
// effective order today for 10 XAU
```
To calculate how much a given amount has demurred up to today:
```
// create a new Amount object
var demAmount = Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
// apply interest
demAmount = demAmount.applyInterest(new Date());
// get the current amount:
console.log(demAmount.to_json());
Where Does Demurrage Go?
------------------------
To put it simply, the value that depreciates due to demurrage becomes
the property of the gateway issuing the currency. This means that, when
someone holding a balance of currency goes to redeem that currency from
the Gateway, the amount they get is the post-demurrage amount (the
"display amount") relative to the current moment in time.
Note that, when the Gateway issues currency with demurrage, it must also
adjust the values it issues, so that the amount sent to the rippled
server and written in the ledger is already adjusted for demurrage up to
the point in time it was issued.
Demurrage and Money-Making Offers
---------------------------------
Demurrage ensures that particular currencies "drift" in value slowly
over time, so that the same amount on the ledger is worth increasingly
less over time when redeemed. (A balance will never go negative, but it
will get closer and closer to zero value.) However, offers are not
automatically updated to compensate for demurrage that accumulated in
the time since the offer was made. This means that, if you are making an
offer to buy a demurraging currency, you must occasionally adjust your
offer to ask for higher ledger amounts to get the same post-demurrage
actual value. However, we expect this to be a complete non-issue because
the relative value of currencies (especially pseudo-currency commodities
such as precious metals) fluctuates much faster than typical rates of
demurrage.
Since Ripple client applications already adjust for demurrage when
taking human inputs to make an offer, most users will not have to do
anything different when making offers.
Currency Format Details
-----------------------
Currency with demurrage or interest is represented as a 160-bit value
that begins with the value `0x01`. This sets it apart from hashes (which
are always `0x80` or higher) as well as standard currency (`0x00`) and
other currency types we may define in the future (`0x02-0x7F`).
Demurraging currency has the following format:
01 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
CURCODE- DATE------- RATE------------------- RESERVED---
- CURCODE is the 3-byte currency code, such as "USD"
- DATE is no longer used, and should always be 0.
- RATE is the e-folding time, formatted as an IEEE Double, represented
in hex. For example, -6291418827.045599 becomes "C1F76FF6ECB0BAC6".
- RESERVED is reserved for future use. It should always be given as 0,
but other values should be accepted for forward-compatibility.

View File

@@ -284,15 +284,14 @@ Contact [partners@ripple.com](mailto:partners@ripple.com) to see how Ripple Labs
There are several interfaces you can use to connect to Ripple, depending on your needs and your existing software:
* [`rippled`](rippled-apis.html) provides JSON-RPC and WebSocket APIs that can be used as a low-level interface to all core Ripple functionality.
* The official client library to rippled, [ripple-lib](https://github.com/ripple/ripple-lib) is available for JavaScript, and provides extended convenience features.
* [Ripple-REST](ripple-rest.html) provides an easy-to-use RESTful API on top of `rippled`. In particular, Ripple-REST is designed to be easier to use from statically-typed languages.
* [RippleAPI](rippleapi.html) provides a simple API for JavaScript applications.
## Tool Security ##
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.
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, *and* 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 `rippled` server; or you can use a client application such as `ripple-lib` to perform local signing before submitting your transactions to a third-party server.
The examples in this document show API methods that include an account secret. This is only safe if you control `rippled` server yourself, *and* 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 use [RippleAPI](rippleapi.html) to perform local signing before submitting your transactions to a third-party server.
## DefaultRipple ##
@@ -301,9 +300,7 @@ The DefaultRipple flag controls whether the balances held in an account's trust
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.
*Note:* Ripple-REST (as of version 1.4.0) does not yet support retrieving or setting the DefaultRipple flag.
The following is an example of using a local [`rippled` JSON-RPC API](ripple-rest.html#update-account-settings) to enable the DefaultRipple flag:
The following is an example of using a locally-hosted `rippled`'s [`submit` command](rippled-apis.html#submit) to send an AccountSet transaction to enable the DefaultRipple flag:
Request:
@@ -371,22 +368,30 @@ Enable the [RequireDest](#requiredest) flag on your hot and cold wallet accounts
## DisallowXRP ##
The DisallowXRP flag (`disallow_xrp` 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.
The DisallowXRP setting (`disallowIncomingXRP` in RippleAPI) 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.
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.
The following is an example of a [Ripple-REST Update Account Settings request](ripple-rest.html#update-account-settings) to enable the DisallowXRP flag:
The following is an example of using a locally-hosted `rippled`'s [`submit` command](rippled-apis.html#submit) to send an AccountSet transaction to enable the DisallowXRP flag:
Request:
```
POST /v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/settings?validated=true
POST http://localhost:8088/
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"settings": {
"disallow_xrp": true
}
"method": "submit",
"params": [
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"tx_json": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "10000",
"Flags": 0,
"SetFlag": 3,
"TransactionType": "AccountSet"
}
}
]
}
```
@@ -395,157 +400,159 @@ _(**Reminder:** Don't send your secret to a server you do not control.)_
Response:
```
200 OK
{
"success": true,
"settings": {
"hash": "AC0F7D7735CDDC6D859D0EC4E96A571F71F7481750F4C6C975FC8075801A6FB5",
"ledger": "10560577",
"state": "validated",
"require_destination_tag": false,
"require_authorization": false,
"disallow_xrp": true
}
"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": "12000322000000002400000164202100000003684000000000002710732103AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB74473045022100C2E38177E92C3998EB2C22978595784BC4CABCF7D57DE71FCF6CF162FB683A1D02205942D42C440D860B4CF7BB0DF77E4F2C529695854835B2F76DC2D09644FCBB2D81144B4E9C06F24296074F7BC48F92A97916C6DC5EA9",
"tx_json": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "10000",
"Flags": 0,
"Sequence": 356,
"SetFlag": 3,
"SigningPubKey": "03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB",
"TransactionType": "AccountSet",
"TxnSignature": "3045022100C2E38177E92C3998EB2C22978595784BC4CABCF7D57DE71FCF6CF162FB683A1D02205942D42C440D860B4CF7BB0DF77E4F2C529695854835B2F76DC2D09644FCBB2D",
"hash": "096A89DA55A6A1C8C9EE1BCD15A8CADCC52E6D2591393F680243ECEB93161B33"
}
}
}
```
The value `"disallow_xrp": true` indicates that the DisallowXRP flag is enabled. A successful response shows `"state": "validated"` when the change has been accepted into a validated Ripple ledger.
## RequireDest ##
The `RequireDest` flag (`require_destination_tag` in Ripple-REST) is designed to prevent users from sending payments to your account while accidentally forgetting the [destination tag](#source-and-destination-tags) 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.
The `RequireDest` setting (`requireDestinationTag` in RippleAPI) is designed to prevent users from sending payments to your account while accidentally forgetting the [destination tag](#source-and-destination-tags) that identifies who should be credited for the payment. When enabled, the Ripple Consensus Ledger rejects any payment to your account that does not specify a destination tag.
We recommend enabling the RequireDest flag on all gateway hot and cold wallets.
We recommend enabling the `RequireDest` flag on all gateway hot and cold wallets.
The following is an example of a [Ripple-REST Update Account Settings request](ripple-rest.html#update-account-settings) to enable the RequireDest flag.
The following is an example of using a locally-hosted `rippled`'s [`submit` command](rippled-apis.html#submit) to send an AccountSet transaction to enable the `RequireDest` flag:
Request:
```
POST /v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/settings?validated=true
POST http://localhost:5005/
Content-Type: application/json
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"settings": {
"require_destination_tag": true
}
"method": "submit",
"params": [
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"tx_json": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "15000",
"Flags": 0,
"SetFlag": 1,
"TransactionType": "AccountSet"
}
}
]
}
```
_(**Reminder:** Don't send your secret to a server you do not control.)_
Response:
```
200 OK
{
"success": true,
"settings": {
"hash": "F3D2EE87D597BA50EA3A94027583110925E8BAAFE41511F937D65423B18BC2A3",
"ledger": "10560755",
"state": "validated",
"require_destination_tag": true,
"require_authorization": false,
"disallow_xrp": false
}
"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": "12000322000000002400000161202100000003684000000000003A98732103AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB74473045022100CD9A87890ADFAC49B8F69EDEC4A0DB99C86667883D7579289B06DAA4B81BF87E02207AC3FEEA518060AB2B538D330614D2594F432901F7C011D7EB92F74383E5340F81144B4E9C06F24296074F7BC48F92A97916C6DC5EA9",
"tx_json": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "15000",
"Flags": 0,
"Sequence": 353,
"SetFlag": 3,
"SigningPubKey": "03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB",
"TransactionType": "AccountSet",
"TxnSignature": "3045022100CD9A87890ADFAC49B8F69EDEC4A0DB99C86667883D7579289B06DAA4B81BF87E02207AC3FEEA518060AB2B538D330614D2594F432901F7C011D7EB92F74383E5340F",
"hash": "59025DD6C9848679BA433448A1DD95833F2F4B64B03E214D074C7A5B6E3E3E70"
}
}
}
```
The value `"require_destination_tag": true` indicates that the RequireDest flag has been enabled. A successful response shows `"state": "validated"` when the change has been accepted into a validated Ripple ledger.
## RequireAuth ##
The `RequireAuth` flag (`require_authorization` in Ripple-REST) prevents a Ripple account's issuances from being held by other users unless the issuer approves them.
The `RequireAuth` setting (`requireAuthorization` in RippleAPI) prevents a Ripple account's issuances from being held by other users unless the issuer approves them.
We recommend enabling RequireAuth for all hot wallet (and warm wallet) accounts, as a precaution. Separately, the Authorized Accounts feature involves [setting the RequireAuth flag on your cold wallet](#with-cold-wallets).
We recommend always [enabling `RequireAuth`](#enabling-requireauth) for hot wallet and warm 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.
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.
If you want to use the [Authorized Accounts](#authorized-accounts) feature, you must also enable `RequireAuth` on your cold wallet. When using Authorized Accounts, your cold wallet must [submit a `TrustSet` transaction to approve each trust line](#authorizing-trust-lines) that customers open to your cold wallet.
### With Hot Wallets ###
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 Consensus Ledger.
We recommend enabling `RequireAuth` 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.
### Enabling RequireAuth ###
The following is an example of a [Ripple-REST Update Account Settings request](ripple-rest.html#update-account-settings) 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.)
The following is an example of using a locally-hosted `rippled`'s [`submit` command](rippled-apis.html#submit) to send an AccountSet transaction to enable the RequireAuth flag: (This method works the same way regardless of whether the account is used as a hot wallet or cold wallet.)
Request:
```
POST /v1/accounts/rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW/settings?validated=true
POST http://localhost:5005/
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"settings": {
"require_authorization": true
}
"method": "submit",
"params": [
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"tx_json": {
"Account": "rUpy3eEg8rqjqfUoLeBnZkscbKbFsKXC3v",
"Fee": "15000",
"Flags": 0,
"SetFlag": 2,
"TransactionType": "AccountSet"
}
}
]
}
```
_(**Reminder:** Don't send your secret to a server you do not control.)_
Response:
### Authorizing Trust Lines ###
```
{
"success": true,
"settings": {
"hash": "687702E0C3952E2227B2F7A0B34933EAADD72A572B234D31360AD83D3F193A78",
"ledger": "10596929",
"state": "validated",
"require_destination_tag": false,
"require_authorization": true,
"disallow_xrp": false
}
}
```
If you are using the [Authorized Accounts](#authorized-accounts) feature, then after enabling the `RequireAuth` setting for your cold wallet, users cannot hold balances issued by your cold wallet unless you first authorize their trust lines.
### With Cold Wallets ###
To authorize a trust line, submit a TrustSet transaction from your cold wallet, with the user to trust as the `issuer` of the `LimitAmount`. Leave the `value` (the amount to trust them for) as **0**, and enable the [tfSetfAuth](transactions.html#trustset-flags) flag for the transaction.
You may also enable `RequireAuth` for your cold wallet in order to use the [Authorized Accounts](#authorized-accounts) feature. The procedure to enable the RequireAuth flag for a cold wallet is the same as [with hot wallets](#with-hot-wallets).
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.
The following is an example of using the [Ripple-REST Grant Trustline method](ripple-rest.html#grant-trustline) to authorize the (customer) account rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn to hold issuances of USD from the (cold wallet) account rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW:
The following is an example of using a locally-hosted `rippled`'s [`submit` command](rippled-apis.html#submit) to send a TrustSet transaction authorizing the (customer) account rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn to hold issuances of USD from the (cold wallet) account rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW:
Request:
```
POST /v1/accounts/rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW/trustlines?validated=true
POST http://localhost:8088/
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"trustline": {
"limit": "0",
"currency": "USD",
"counterparty": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"authorized": true
}
}
```
_(**Reminder:** Don't send your secret to a server you do not control.)_
Response:
```
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"
"method": "submit",
"params": [
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"tx_json": {
"Account": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"Fee": "15000",
"TransactionType": "TrustSet",
"LimitAmount": {
"currency": "USD",
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value": 0
},
"Flags": 65536
}
}
]
}
```
A successful response shows `"state": "validated"` when the change has been accepted into a validated Ripple ledger.
_(**Reminder:** Don't send your secret to a server you do not control.)_
## Robustly Monitoring for Payments ##
@@ -553,169 +560,175 @@ In order to robustly monitor incoming payments, gateways should do the following
* 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.
* 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 `tesSUCCESS` can change non-XRP balances. Only transactions from a validated ledger are final.
* [Look out for Partial Payments](https://ripple.com/files/GB-2014-06.pdf "Partial Payment Flag Gateway Bulletin"). If an incoming transaction has a `destination_balance_changes` field (Ripple-REST) or a `meta.delivered_amount` field (WebSocket/JSON-RPC), then use that to see how much money *actually* 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 `"partial_payment": true` in REST, and `tfPartialPayment` in WebSocket/JSON-RPC)
* [Look out for Partial Payments](https://ripple.com/knowledge_center/partial-payment-flag/ "Partial Payment Flag Gateway Bulletin"). Payments with the partial-payment flag enabled can be considered "successful" if any non-zero amount is delivered, even miniscule amounts.
* In `rippled`, check the transaction for a `meta.delivered_amount` field. If present, that field indicates how much money *actually* got delivered to the `Destination` account.
* In RippleAPI, you can search the `outcome.BalanceChanges` field to see how much the destination account received. In some cases, this can be divided into multiple parts on different trust lines.
* Some transactions modify your balances without being payments directly to or from one of your accounts. For example, if ACME sets a nonzero [TransferRate](#transferrate), then ACME's cold wallet's outstanding obligations decrease each time Bob and Charlie exchange ACME issuances. See [TransferRate](#transferrate) for more information.
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.
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.
* Use the [Get Account Balances method](ripple-rest.html#get-account-balances) (Ripple-REST) or the [`account_lines` command](rippled-apis.html#account-lines) (rippled) to check your balances.
* Use [`rippled`'s `account_lines` command](rippled-apis.html#account-lines) or [RippleAPI's `getTrustlines` method](rippleapi.html#gettrustlines) to check your balances.
* If you have a [TransferRate](#transferrate) set, then your obligations within the Ripple network decrease slightly whenever other users transfer your issuances among themselves.
## TransferRate ##
The *TransferRate* setting (`transfer_rate` in Ripple-REST) defines a fee to charge for transferring issuances from one Ripple account to another. See [Transfer Fees](https://ripple.com/knowledge_center/transfer-fees/) in the Knowledge Center for more information.
The *TransferRate* setting (`transferRate` in RippleAPI) defines a fee to charge for transferring issuances from one Ripple account to another. See [Transfer Fees](https://ripple.com/knowledge_center/transfer-fees/) in the Knowledge Center for more information.
The following is an example of a [Ripple-REST Update Account Settings request](ripple-rest.html#update-account-settings) to set the TransferRate for a fee of 0.5%.
The following is an example of using a locally-hosted `rippled`'s [`submit` command](rippled-apis.html#submit) to send an AccountSet transaction for cold wallet account rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW, setting the TransferRate to charge a fee of 0.5%.
Request:
```
POST /v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/settings?validated=true
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"settings": {
"transfer_rate": 1.005
}
"method": "submit",
"params": [
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"tx_json": {
"Account": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"Fee": "10000",
"Flags": 0,
"TransferRate": 1005000000,
"TransactionType": "AccountSet"
}
}
]
}
```
_(**Reminder:** Don't send your secret to a server you do not control.)_
Response:
```
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"
"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": "1200032200000000240000000F2B3BE71540684000000000002710732102B3EC4E5DD96029A647CFA20DA07FE1F85296505552CCAC114087E66B46BD77DF74473045022100AAFC3360BE151299523A93F445D5F6EB58AF5A4F8586C8B7818D6C6069660B40022022F46BCDA8FEE256AEB0BA2E92947EF4571F92354AB703E3E6D77FEF7ECBF64E8114204288D2E47F8EF6C99BCC457966320D12409711",
"tx_json": {
"Account": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"Fee": "10000",
"Flags": 0,
"Sequence": 15,
"SigningPubKey": "02B3EC4E5DD96029A647CFA20DA07FE1F85296505552CCAC114087E66B46BD77DF",
"TransactionType": "AccountSet",
"TransferRate": 1005000000,
"TxnSignature": "3045022100AAFC3360BE151299523A93F445D5F6EB58AF5A4F8586C8B7818D6C6069660B40022022F46BCDA8FEE256AEB0BA2E92947EF4571F92354AB703E3E6D77FEF7ECBF64E",
"hash": "24360352FBF5597F313E5985C1766BB4A0D277CE63219AC0C0D81014C1E663BB"
}
}
}
```
The field `transfer_rate` in the `settings` object should have the value you set. A successful response shows `"state": "validated"` when the change has been accepted into a validated Ripple ledger.
### TransferRate with Hot and Warm Wallets ###
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 `source_amount` plus `slippage` (Ripple-REST) or the `SendMax` (rippled) parameters higher than the destination amount.
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. In other words, your hot wallet must pay back a little of the money your cold wallet issued it each time you make a payment.
**Note:** 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 `source_amount` and `destination_amount` of 5 EUR.ACME (and `slippage` 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 `source_amount` plus `slippage` 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.
* In `rippled`'s APIs, you can accomplish this by setting the [`SendMax` transaction parameter](transactions.html#payment) higher than the destination `Amount` parameter.
* In RippleAPI, you can accomplish this by setting the `source.maxAmount` parameter higher than the `destination.amount` parameter; or, by setting the `source.amount` parameter higher than the `destination.minAmount` parameter.
**Note:** 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 to deliver 5 EUR.ACME from a user account to ACME's cold wallet would still only cost 5 EUR.ACME. However, the user would need to send 5.05 EUR.ACME in order to deliver 5 EUR.ACME to the hot wallet.) If you accept payments out of Ripple through both accounts, you may want to adjust the amount you credit users in your external system when customers send payments through the hot wallet, to compensate for the TransferRate the customer pays.
## Sending Payments to Users ##
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 **not using** 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 _all_ possible paths to the destination account, including ones that have a higher `source_amount` than necessary.
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.
The following template can be used with Ripple-REST's [Submit Payment method](ripple-rest.html#submit-payment). You should also follow the [reliable transaction submission](#reliable-transaction-submission) guidelines and persist a copy of the transaction before submitting it.
One common pitfall is performing pathfinding before sending sending a payment to users in Ripple. If you specify the issuers correctly, the [default paths](paths.html#default-paths) are sufficient to deliver the currency as intended.
The following is an example of using a locally-hosted `rippled`'s [`submit` command](rippled-apis.html#submit) to send a payment from the hot wallet rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn to the user account raKEEVSGnKSD9Zyvxu4z6Pqpm4ABH8FS6n, sending and delivering funds issued by the cold wallet rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW.
Request:
```
POST /v1/accounts/<HOT WALLET ADDRESS>/payments
{
"secret": <HOT WALLET SECRET KEY>,
"client_resource_id": <UNIQUE CLIENT RESOURCE ID>,
"payment": {
"source_account": <HOT WALLET ADDRESS>,
"source_amount": {
"value": <DESTINATION AMOUNT, for example "100">,
"currency": <CURRENCY>,
"issuer": <COLD WALLET ADDRESS>
},
"source_slippage": <DESTINATION AMOUNT * TRANSFER FEE, for example "0.5">,
"source_tag": <OPTIONAL SOURCE TAG>,
"destination_account": <CUSTOMER ADDRESS>,
"destination_amount": {
"value": <DESTINATION AMOUNT>,
"currency": <CURRENCY>,
"issuer": <COLD WALLET ADDRESS>
}
}
"method": "submit",
"params": [{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"tx_json": {
"TransactionType": "Payment",
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Destination": "raKEEVSGnKSD9Zyvxu4z6Pqpm4ABH8FS6n",
"Amount": {
"currency": "USD",
"value": "0.13",
"issuer": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW"
},
"SendMax": {
"currency": "USD",
"value": "0.13065",
"issuer": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW"
},
"Fee": "10000"
}
}]
}
```
*Reminder: Don't send your secret to a server you do not control.*
In particular, note the following features of the payment object:
Response:
- No `paths` 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.
- The `issuer` of both the `source_amount` and the `destination_amount` is the cold wallet. This ensures that the transaction sends and delivers issuances from the cold wallet account, and not from some other gateway.
- The `source_amount` uses the same `value` as the `destination_amount`. The [transfer fee](#transferrate), if there is one, is covered by the `slippage` field.
```
{
"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": "1200002280000000240000016561D4449E57D63540000000000000000000000000005553440000000000204288D2E47F8EF6C99BCC457966320D1240971168400000000000271069D444A4413C6628000000000000000000000000005553440000000000204288D2E47F8EF6C99BCC457966320D12409711732103AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB7446304402207B75D91DC0EEE613A94E05FD5D031568D8A763E99697FF6328745BD226DA7D4E022005C75D7215FD62CB8E46C55B29FCA8E3FC62FDC55DF300597089DD29863BD3CD81144B4E9C06F24296074F7BC48F92A97916C6DC5EA983143A4C02EA95AD6AC3BED92FA036E0BBFB712C030C",
"tx_json": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Amount": {
"currency": "USD",
"issuer": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"value": "0.13"
},
"Destination": "raKEEVSGnKSD9Zyvxu4z6Pqpm4ABH8FS6n",
"Fee": "10000",
"Flags": 2147483648,
"SendMax": {
"currency": "USD",
"issuer": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"value": "0.13065"
},
"Sequence": 357,
"SigningPubKey": "03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB",
"TransactionType": "Payment",
"TxnSignature": "304402207B75D91DC0EEE613A94E05FD5D031568D8A763E99697FF6328745BD226DA7D4E022005C75D7215FD62CB8E46C55B29FCA8E3FC62FDC55DF300597089DD29863BD3CD",
"hash": "37B4AA5C77A8EB889164CA012E6F064A46B6B7B51677003FC3617F614608C60B"
}
}
}
```
In particular, note the following features of the [Payment Transaction](transactions.html#payment):
- No `Paths` field. The payment will only succeed if it can use a [default path](paths.html#default-paths), which is preferable. Using less direct paths can become much more expensive.
- The `issuer` of both the `SendMax` and the `Amount` is the cold wallet. This ensures that the transaction sends and delivers issuances from the cold wallet account, and not from some other gateway.
- The `value` of the `SendMax` amount is slightly higher than the destination `Amount`, in order to compensate for the [transfer fee](#transferrate). In this case, the transfer fee is 0.5%, so the `SendMax` amount is exactly 1.005 times the destination `Amount`.
## Bouncing Payments ##
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.
The first requirement to bouncing payments is [robustly monitoring for incoming payments](#robustly-monitoring-for-payments). 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 [Partial Payment Flag Gateway Bulletin](https://ripple.com/files/GB-2014-06.pdf) explains how to avoid a common problem.
The first requirement to bouncing payments is [robustly monitoring for incoming payments](#robustly-monitoring-for-payments). 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 [Partial Payment Flag Gateway Bulletin](https://ripple.com/knowledge_center/partial-payment-flag/) explains how to avoid a common problem.
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.
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 Consensus Ledger. 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.
To send a Partial Payment in Ripple-REST, set the `partial_payment` field to true in the object returned by the [Prepare Payment method](ripple-rest.html#prepare-payment) before submitting it. Set the `source_amount` to be equal to the `destination_amount`. and the `slippage` to `"0"`. As long as your hot and cold wallets only send the currencies that your cold wallet issues, you should also remove the `paths` 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 `issuer` field of the `destination_amount` in the object you get back.)
* To send a Partial Payment using `rippled`, enable the [tfPartialPayment flag](transactions.html#payment-flags) on the transaction. Set the `Amount` field to the amount you received and omit the `SendMax` field.
* To send a Partial Payment using RippleAPI, set the `allowPartialPayment` field of the [Payment object](rippleapi.html#payment) to `true`. Set the `source.maxAmount` and `destination.amount` both equal to the amount you received.
It is conventional that you take the Source Tag from the incoming payment (`source_tag` in Ripple-REST) and use that value as the Destination Tag (`destination_tag` in Ripple-REST) for the return payment.
It is conventional that you take the `SourceTag` field from the incoming payment (`source.tag` in RippleAPI) and use that value as the `DestinationTag` field (`destination.tag` in RippleAPI) for the return payment.
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.
The following is an example of a [Ripple-REST Submit Payment request](ripple-rest.html#submit-payment) to send a return payment. Particularly important fields include the [*destination_tag*](#source-and-destination-tags), setting `"slippage": "0"`, `"partial_payment": true`, the lack of a `"paths"` field, and the `issuer` of the `destination_amount` being the cold wallet:
```
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
}
}
```
_(**Reminder:** Don't send your secret to a server you do not control.)_
## Setting Trust Lines in Ripple Trade ##
As part of the [Hot and Cold Wallets](#hot-and-cold-wallets) 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.
1. Log in and go to the **Fund** tab:
![Fund tab](img/connectgateway_01.png)
2. Click **Gateways** in the sidebar:
![Gateways](img/connectgateway_02.png)
3. Enter the Ripple Name or Ripple Address of the Gateway's **cold wallet**, and click **Save**.
![Enter gateway's name or address, then save](img/connectgateway_03.png)
4. Enter the Ripple Trade password, and click **Submit**. (This allows access to send a transaction to the Ripple Network to create the trust line.)
![Enter password and submit](img/connectgateway_04.png)
5. 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.
![Gateway connected](img/connectgateway_05.png)
## Reliable Transaction Submission ##
@@ -727,16 +740,15 @@ The goal of reliably submitting transactions is to achieve the following two pro
In order to achieve this, there are several steps you can take when submitting transactions:
* Persist details of the transaction before submitting it.
* Use the `LastLedgerSequence` parameter. (Ripple-REST and ripple-lib do this by default.)
* Use the `LastLedgerSequence` parameter. (RippleAPI does this by default.)
* Resubmit a transaction if it has not appeared in a validated ledger whose sequence number is less than or equal to the transaction's `LastLedgerSequence` parameter.
For additional information, consult the [Reliable Transaction Submission](reliable_tx.html) guide.
## ripple.txt and host-meta ##
## ripple.txt ##
The [ripple.txt](https://wiki.ripple.com/Ripple.txt) and host-meta standards provide a way to publish information about your gateway so that automated tools and applications can read and understand it.
The [ripple.txt](https://wiki.ripple.com/Ripple.txt) standard provides a way to publish information about your gateway so that automated tools and applications can read and understand it.
For example, if you run a validating `rippled` 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.
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.)

View File

@@ -1,30 +0,0 @@
# Introduction #
Ripple is a decentralized, peer-to-peer network for moving value using cryptographic technology. For more on the big picture, consult [ripple.com](https://ripple.com/) and check out [our blog](https://ripple.com/blog/).
# Ripple Client Applications #
The official web client for the Ripple Network is available at [https://rippletrade.com/](). There is also an official downloadable client at [http://download.ripple.com/]().
In order to activate your account, you must fund it with enough XRP to meet the account reserve (currently 20 XRP). You can do this in a few different ways:
* You can buy XRP with Bitcoins in the Ripple Trade client, under the [Fund](https://www.rippletrade.com/#/fund) tab.
* You can have someone who is already on the network send a payment to your account's address.
* Keep an eye out for promotions that give away free XRP to developers.
# Ripple APIs #
If you intend to act as a gateway, or if you are a developer with great ideas of how to use the Ripple Network, you will probably want to build a custom client application that you or your customers can use to send, receive, or observe funds on the Ripple Network.
Connecting to the Ripple Network generally means communicating with the Ripple Server software, [`rippled`](https://github.com/ripple/rippled) (pronounced "ripple-dee"). To get started, you can try running a few calls to retrieve information from public servers using the [Ripple API Tool](https://ripple.com/tools/api) or you try downloading and running your own instance of `rippled`.
If you are building your own client, you have several options of interfaces that you can use to interact with the Ripple Network:
| Tool | Summary | Interface | Abstraction Level | Pros | Cons |
|------|---------|-----------------------|-------------------|------|------|
| [gatewayd](https://github.com/ripple/gatewayd) | Skeleton for implementing gateway functionality as a Node.js application | HTTP interface | Very high abstraction | ✓ Most functionality needed to operate a gateway is already implemented | ✗ Only intended for gateways <br> ✗ Requires Node.js |
| [Ripple-REST](?p=ripple-rest-api) | RESTful interface to `rippled` as a Node.js application | HTTP interface | High abstraction | ✓ Simple robust transaction submission <br> ✓ Broad HTTP-client support | ✗ Lacks access to a few features like viewing currency exchange offers <br> ✗ Requires Node.js |
| [ripple-lib](https://github.com/ripple/ripple-lib) | Reference implementation for accessing the WebSocket API | Javascript library | Moderate abstraction | ✓ Simple robust transaction submission<br> ✓ Good balance of simplicity and power | ✗ Javascript only (Clients for other languages are in progress) |
| [rippled WebSocket API](?p=web-sockets-api) | Powerful, asynchronous API built on the WebSocket protocol | [WebSocket](http://en.wikipedia.org/wiki/Websocket) interface | Low abstraction | ✓ Access to all Ripple functionality <br> ✓ Can be pushed ordered stream data | ✗ Fewer convenient abstractions <br> ✗ WebSocket clients are rare outside of Javascript |
| [rippled JSON-RPC API](?p=web-sockets-api) <!--note: that's not a typo, the websocket and json-rpc docs are on the same page--> | Powerful, synchronous API built on the [JSON-RPC convention](http://json-rpc.org/) | HTTP interface | Low abstraction | ✓ Access to almost all Ripple functionality <br> ✓ Broad HTTP-client support | ✗ Fewer convenient abstractions <br> ✗ Callbacks may arrive out of order <br> ✗ No incremental pathfinding |

View File

@@ -147,7 +147,7 @@ There are two kinds of Directories:
Example Directories:
<div class='multicode'>
<!-- <div class='multicode'> -->
*Offer Directory*
@@ -184,7 +184,7 @@ Example Directories:
}
```
</div>
<!-- </div> -->
| Name | JSON Type | [Internal Type][] | Description |
|-------------------|-----------|---------------|-------------|

View File

@@ -1,6 +1,6 @@
# Reliable Transaction Submission
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.
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. You should submit transactions to trusted (locally operated) `rippled` servers.
The best practices detailed in this document allow applications to submit transactions to the Ripple network while achieving:
@@ -17,7 +17,7 @@ These types of errors can potentially lead to serious problems. For example, an
## Background
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.-->
The Ripple protocol provides a ledger shared across all nodes in the network. Through a [process of consensus and validation](https://ripple.com/knowledge_center/the-ripple-ledger-consensus-process/), the network agrees on order in which transactions are applied to (or omitted from) the ledger.
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 [transaction cost](tx-cost.html) increases after an application sends a transaction. If the transaction cost 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 transaction cost 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.
@@ -28,33 +28,37 @@ Applications face additional challenges, in the event of power or network loss,
### Transaction Timeline
Ripple provides several APIs for submitting transactions ([rippled](rippled-apis.html), ripple-lib, [Ripple-REST](ripple-rest.html)). Regardless of the API used, the transaction is applied to the ledger as follows.
Ripple provides several APIs for submitting transactions, including [`rippled`](rippled-apis.html), and [RippleAPI](rippleapi.html). Regardless of the API used, the transaction is applied to the ledger as follows.
1. A transaction is created and signed by account owner.
2. That transaction is submitted to the network as a candidate transaction.
1. An account owner creates and signs a transaction.
2. The owner submits the transaction to the network as a candidate transaction.
- Malformed or nonsensical transactions are rejected immediately.
- Well formed transactions may provisionally succeed, then later fail.
- Well formed transactions may provisionally fail, then later succeed.
3. Through consensus and validation, the transaction is applied to the ledger. Even some failed transactions are applied in order to enforce a cost for being propagated through the network.
- Well-formed transactions may provisionally succeed, then later fail.
- Well-formed transactions may provisionally fail, then later succeed.
- Well-formed transactions may provisionally succeed, and then later succeed in a slightly different way. (For example, by consuming a different offer and achieving a better or worse exchange rate than the provisional execution.)
3. Through consensus and validation, the transaction is applied to the ledger. Even some failed transactions are applied, in order to enforce a cost for being propagated through the network.
4. The validated ledger includes the transaction, and its effects are reflected in the ledger state.
- Transaction results are no longer provisional, success or failure is now final and immutable.
*Note:* 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.
*Note:* When submitting a transaction via `rippled`, 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.
Ripple APIs may return provisional results based on candidate transactions. Applications must not confuse these with the final, *immutable*, 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.
APIs may return provisional results based on the result of applying candidate transactions to the current, in-progress ledger. Applications must not confuse these with the final, *immutable*, 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.
While applying transactions, Ripple network nodes work with the *last validated ledger*, 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.
While applying transactions, `rippled` servers use the *last validated ledger*, 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 in canonical order, resulting in a new validated ledger. This new validated ledger instance and the ones that preceded it comprise the ledger history.
Each validated ledger instance has a sequence number, which is one greater than the sequence number of the preceding instance. Each ledger also has an identifying hash value, which is uniquely determined from its contents. There may be many different versions of in-progress ledgers, which have the same sequence number but different hash values. Only one version can ever be validated.
Each validated ledger has a canonical order in which transactions apply. This order is deterministic based on the final transaction set of the ledger. In contrast, each `rippled` server's in-progress ledger is calculated incrementally, as transactions are received. The order in which transactions execute provisionally is usually not the same as the order in which transactions execute to build a new validated ledger. This is one reason why the provisional outcome of a transaction may be different than the final result. For example, a payment may achieve a different final exchange rate depending on whether it executes before or after another payment that would consume the same offer.
### LastLedgerSequence
[`LastLedgerSequence`](transactions.html#lastledgersequence) 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.
[`LastLedgerSequence`](transactions.html#lastledgersequence) is an optional parameter of all transactions. This instructs the Ripple Consensus Ledger that a transaction must be validated on or before a specific ledger instance. The Ripple Consensus Ledger never includes a transaction in a ledger instance whose sequence number is higher than the transaction's `LastLedgerSequence` parameter.
Use the `LastLedgerSequence` 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 `LastLedgerSequence` 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.
Applications using rippled APIs should explicitly specify a `LastLedgerSequence` when submitting transactions. When using ripple-lib or Ripple-REST, a `LastLedgerSequence` is automatically included. Applications using those APIs are recommended to use the `LastLedgerSequence` calculated by the API.
Applications using rippled APIs should explicitly specify a `LastLedgerSequence` when submitting transactions. RippleAPI uses the `maxLedgerVersion` field of [Transaction Instructions](rippleapi.html#transaction-instructions) to specify the `LastLedgerSequence`. RippleAPI automatically provides an appropriate value by default. You can specify `maxLedgerVersion` as `null` to intentionally omit `LastLedgerSequence`, in case you want a transaction that can be executed after an unlimited amount of time.
@@ -121,31 +125,31 @@ For each persisted transaction without validated result:
In order to implement the transaction submission and verification best practices, applications need to perform the following actions.
* Determine the signing account's next sequence number
* 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.
* Decide on a `LastLedgerSequence`
* A transaction's `LastLedgerSequence` is calculated from the last validated ledger sequence number.
* Construct and sign the transaction
* The details of a signed transaction are persisted before submission.
* Submit the transaction
* Initial results are provisional and subject to change.
* Determine the final result of a transaction
* Final results are an immutable part of the ledger history.
1. Determine the signing account's next sequence number
* 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.
3. Decide on a `LastLedgerSequence`
* A transaction's `LastLedgerSequence` is calculated from the last validated ledger sequence number.
3. Construct and sign the transaction
* Persist the details of a signed transaction before submission.
4. Submit the transaction
* Initial results are provisional and subject to change.
5. Determine the final result of a transaction
* Final results are an immutable part of the ledger history.
An application's means of performing these actions depends on the ripple API the application uses. These interfaces may be any of:
An application's means of performing these actions depends on the API the application uses. An application may use any of the following interfaces:
1. [rippled](rippled-apis.html)
2. [ripple-rest](ripple-rest.html)
3. [ripple-lib](https://github.com/ripple/ripple-lib/)
1. [`rippled`'s internal APIs](rippled-apis.html)
2. [RippleAPI](rippleapi.html)
3. Any number of other software APIs layered on top of `rippled`
### rippled - Submitting and Verifying Transactions
#### Determine the Account Sequence
rippled provides the [account_info](rippled-apis.html#account-info) method to learn an account's sequence number in the last validated ledger.
`rippled` provides the [account_info](rippled-apis.html#account-info) method to learn an account's sequence number in the last validated ledger.
Request:
JSON-RPC Request:
```
{
@@ -159,7 +163,7 @@ Request:
}
```
Response:
Response body:
```
{
@@ -184,7 +188,7 @@ Response:
In this example, the account's sequence is **4** (note `"Sequence": 4`, in `"account_data"`) as of the last validated ledger (note `"ledger": "validated"` in the request, and `"validated": "true"` in the response).
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.
If an application were to submit three transactions signed by this account, they would use sequence numbers 4, 5, and 6. In order to submit multiple transactions without waiting for validation of each, an application should keep a running account sequence number.
#### Determine the Last Validated Ledger
@@ -240,7 +244,7 @@ In this example the last validated ledger sequence number is 10268596 (found und
#### Construct the Transaction
`rippled` provides the [RPC sign](rippled-apis.html#sign) 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.
`rippled` provides the [sign method](rippled-apis.html#sign) 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.
Request:
@@ -307,7 +311,7 @@ Applications should persist the transaction's hash before submitting. The resul
#### Submit the transaction
`rippled` provides the [`submit` method](rippled-apis.html#submit), allowing us to submit the signed transaction. The `tx_blob` parameter was returned from the `sign` method.
`rippled` provides the [`submit` method](rippled-apis.html#submit), allowing us to submit the signed transaction. This uses the `tx_blob` parameter that was returned by the `sign` method.
Request:
@@ -468,557 +472,13 @@ The [`server_state` method](rippled-apis.html#server-state) (used earlier to det
}
```
Our example transaction specified `LastLedgerSequence` 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, **and** `tx` returns `txnNotFound`, 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 `LastLedgerSequence`.
Our example transaction specified `LastLedgerSequence` 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, **and** `tx` returns `txnNotFound`, 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 `LastLedgerSequence`.
The server state may indicate a last validated ledger sequence number less than the specified `LastLedgerSequence`. If so, the `txnNotFound` 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.
Finally the server state might indicate one or more gaps in the transaction history. The `completed_ledgers` 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 `LastLedgerSequence`), 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 `txnNotFound` result is immutable.
### Ripple-REST - Submitting and Verifying Payments
The [Ripple-REST API](ripple-rest.html) provides an interface to Ripple via a [RESTful API](https://en.wikipedia.org/wiki/Representational_state_transfer). 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, `LastLedgerSequence`, and transaction cost.
This examples that follow refer to Ripple-REST API for *payments*. The REST methods for setting trust lines and modifying account settings does not follow the same pattern. See [RLJS-126](https://ripplelabs.atlassian.net/browse/RLJS-126) for additional details.
#### Construct the Transaction
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.
```
GET /v1/accounts/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W/payments/paths/rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM/10+FOO+rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W
```
The response (below) indicates one payment path exists. (The `payments[0].paths` is present, and empty, when issuing currency directly.)
```
{
"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
}
```
#### Submit the Payment Transaction
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 `client_resource_id` 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 `client_resource_id` "payment-42".
Before submitting, applications should persist the `client_resource_id`, transaction type, and source account.
Request:
```
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"
}
}
```
***Note: only submit your account secret to a trusted Ripple-REST server.***
Response:
```
{
success: true,
client_resource_id: 'payment-42',
status_url: 'http://127.0.0.1:5990/v1/accounts/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W/payments/payment-42' }
```
The payment result remains provisional at this point. Ripple-REST returns a `status_url` where we can eventually learn the final result of the transaction. The `status_url` is based on the `client_resource_id` persisted by the application before submission, so the application can construct the `status_url` even in the event of a power failure before this response is received.
#### Verify the Payment Transaction
A GET request to the transaction's `status_url` retrieves the result.
Request:
```
GET /v1/accounts/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W/payments/payment-42
```
Response:
```
{
"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
}
```
In this example, `payment.state: "validated"` indicates the transaction is in a validated ledger. Therefore the results are final and immutable. Any other value for `payment.state` indicates provisional results, an application must check again later to determine final results.
In the preceeding example, `payment.result: "tesSUCCESS"` (along with the `"validated"` 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.
#### Verify Missing or Failed Payment Transaction
When a GET request to the transaction's `status_url` returns an error, an application must determine whether the final result of a transaction is failure.
Request:
```
GET /v1/accounts/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W/payments/payment-42
```
Response:
```
{
"message": "Transaction not found.",
"error": "txnNotFound",
"error_type": "transaction",
"success": false
}
```
*Note:* The `txnNotFound` 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 [RLJS-163](https://ripplelabs.atlassian.net/browse/RLJS-163) for updates regarding this behavior.
The `txnNotFound` 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.
Another error response:
```
{
"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
}
```
This `"Transaction not found"` 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.
### ripple-lib - Submitting and Verifying Transactions
[ripple-lib](https://github.com/ripple/ripple-lib) 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 `LastLedgerSequence`.
#### Construct the Transaction
ripple-lib provides a high level API for creating transactions. In this example, a payment:
var tx = remote.createTransaction('Payment', {
account: rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W',
destination: rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM',
amount: 10/FOO/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W'
});
Note that ripple-lib will automatically provide additional details before the transaction is signed and submitted. These details include an account transaction Sequence, `LastLedgerSequence` and transaction cost.
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 [transaction event handlers](https://github.com/ripple/ripple-lib/blob/61afca2337927a4f331ae02770ccdad5d9bdef17/docs/REFERENCE.md#transaction-events). The `presubmit` event handler is appropriate for saving data before a transaction is submitted to the network. During normal operation, a `state` event is emitted whenever the transaction status changes, including final validation.
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 **synchronously** persist transactions details, and should throw an error if unable to save the data.
// 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.
});
#### Submit the Transaction
ripple-lib provides `Transaction.submit()` to both sign and submit a transaction. In order to sign, an application must first call `Remote.setSecret(<account>, <secret>)`. Take care to configure ripple-lib to use local signing, as secrets should only be shared with trusted ripple servers.
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));
}
});
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.
```
- 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 }
```
Note in the preceeding example log output, `tx.state: 'unsubmitted'` indicates the transaction is not yet sent to the network. `tx.submittedIDs` 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 `tx.tx_json.Sequence` and `tx.tx_json.LastLedgerSequence`.
ripple-lib will re-submit a transaction in response to certain errors. Prior to each submission the `presubmit` event provides up-to-date information about the transaction, which applications should persist.
```
- 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' } }
```
The example above shows the transaction after 1 failed attempts, before a second attempt is submitted. Note that `tx.submittedIDs[0]` is the updated hash which applications should persist.
The `tx.result` is the **provisional** result of the **prior** submission attempt. In this example the initial submit failed with `telINSUF_FEE_P`, which could happen if the network adjusts the transaction cost calculated by ripple-lib immediately before the transaction is processed.
#### Verify the Transaction
During normal operation, events emitted by ripple-lib inform an application of the result of a transaction. The `state` event will emit with `tx.state: 'pending'` with a provisional result; and finally if successful with `tx.state: 'validated'` and `tx.finalized: true`.
```
- 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' } }
```
If the final state of a transaction is an error, the state event indicates `tx.state: 'failed'` and `tx.finalized: 'true'`:
```
- 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' } }
```
In the event of a power or network failure, an application may be interrupted before these `state` events are emitted. In these cases, applications should retrieve the status of a transaction by its `hash`, one of the details persisted during an earlier `presubmit` event.
var hash = 'C3306CA3ED1B372EAC8A84A84B52752A4E4912BB1A26AB883E969BC987E4D20E';
remote.requestTransaction(hash, function(err, result) {
if (err) {
console.log(err);
}
else {
console.log(result);
}
});
Result of `remote.requestTransaction(<transaction hash>)`:
```
{ 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 }
```
Note the response from `requestTransaction` includes **`validated: true`**, indicating the result is in a validated ledger and is final. Without `validated: true`, results are *provisional* and may change. This example shows `meta.TransactionResult: 'tesSUCCESS'`, indicating the transaction was successful.
#### Verify Missing Transaction
`Remote.requestTransaction` may return `txNotFound`:
```
{ [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.' }
```
In these cases an application must distinguish between a provisional result and a final result. For this, an application needs the transaction's `initialSubmitIndex` and `LastLedgerSequence`. This information was persisted earlier by the application's `presubmit` event handler. In this example, `initialSubmitIndex` is 10301323 and `LastLedgerSequence` is 10301337.
A call to `Remote.requestServerInfo()` determines whether the server has final, immutable results about the transaction.
remote.requestServerInfo(function(err, result) {
if (err) {
console.log(err);
}
else {
console.log(result);
}
});
Result of `remote.requestServerInfo()`:
```
{ 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 } }
```
In this example, `info.complete_ledgers` 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 `txNotFound` result is final, and the transaction has failed immutably.
If the ledger history were not complete through the `LastLedgerSequence`, the application must wait for that ledger to become validated, or the server to sync a more complete history with the network.
## Additional Resources
- [Transaction Format](transactions.html)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -26,19 +26,19 @@ In this scenario, Salazar (the sender) holds EUR issued by ACME, and wants to de
The transfer fee is represented by a setting on the issuing (**cold wallet**) 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.
## Ripple-REST ##
## RippleAPI ##
In Ripple-REST, the transfer fee is specified in the `transfer_rate` 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 `transfer_rate` of `1.005` is equivalent to a transfer fee of 0.5%. By default, the `transfer_rate` is set at `1.0`, indicating no fee. The value of `transfer_rate` cannot be less than `1.0` or more than `2.0`. However, the value `0` is special: it is equivalent to `1.0`, meaning no fee.
In RippleAPI, the transfer fee is specified in the `transferRate` 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 `transferRate` of `1005000000` is equivalent to a transfer fee of 0.5%. By default, the `transferRate` is set to no fee. The value of `transferRate` cannot be less than `1000000000` or more than `2000000000`. The value `null` is special: it is equivalent to `1000000000`, meaning no fee.
A gateway can use the [Update Account Settings method](https://ripple.com/build/ripple-rest/#update-account-settings) with its cold wallet to change the `transfer_rate` for its issuances.
A gateway can send a [Settings transaction](rippleapi.html#settings) with its cold wallet to change the `transferRate` for its issuances.
You can check an account's `transfer_rate` with the [Get Account Settings method](https://ripple.com/build/ripple-rest/#get-account-settings).
You can check an account's `transferRate` with the [getSettings method](rippleapi.html#getsettings).
## rippled ##
In `rippled`'s JSON-RPC and WebSocket APIs, the transfer fee is specified in the `TransferRate` 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 `TransferRate` of `1005000000` is equivalent to a transfer fee of 0.5%. By default, the `transfer_rate` is set at `1000000000`, indicating no fee. The value of `TransferRate` cannot be less than `1000000000` or more than `2000000000`. However, value `0` is special: it is equivalent to `1000000000`, meaning no fee.
In `rippled`'s JSON-RPC and WebSocket APIs, the transfer fee is specified in the `TransferRate` 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 `TransferRate` of `1005000000` is equivalent to a transfer fee of 0.5%. By default, the `TransferRate` is set at `1000000000`, indicating no fee. The value of `TransferRate` cannot be less than `1000000000` or more than `2000000000`. However, value `0` is special: it is equivalent to `1000000000`, meaning no fee.
A gateway can submit an [AccountSet transaction](https://ripple.com/build/transactions/#accountset) from its cold wallet to change the `TransferRate` for its issuances.
A gateway can submit an [AccountSet transaction](transactions.html#accountset) from its cold wallet to change the `TransferRate` for its issuances.
You can check an account's `TransferRate` with the [account_info command](https://ripple.com/build/rippled-apis/#account-info). If the `TransferRate` is omitted, then that indicates no fee.
You can check an account's `TransferRate` with the [`account_info` command](rippled-apis.html#account-info). If the `TransferRate` is omitted, then that indicates no fee.

View File

@@ -1,13 +0,0 @@
Ripple Whitepapers
==================
Ripple has published several whitepapers on the benefits and workings of the Ripple technology stack. The following is a brief selection of useful papers:
* [Ripple Primer](https://ripple.com/ripple_primer.pdf) (PDF)
* [Gateway whitepaper](https://ripple.com/ripple-gateways.pdf) (PDF)
* [Market makers whitepaper](https://ripple.com/ripple-mm.pdf) (PDF)
* [Financial institutions whitepaper](https://ripple.com/ripple-FIs.pdf) (PDF)
* [The Ripple Protocol: A Deep Dive for Finance Professionals](https://ripple.com/files/ripple_deep_dive_final.pdf) (PDF)
* [The Ripple Protocol Consensus Algorithm](https://ripple.com/files/ripple_consensus_whitepaper.pdf) (PDF)
You can find further information about Ripple in the [Knowledge Center](https://ripple.com/knowledge-center/).