Re-level non-docs content to top of repo and rename content→docs

This commit is contained in:
mDuo13
2024-01-31 16:24:01 -08:00
parent f841ef173c
commit c10beb85c2
2907 changed files with 1 additions and 1 deletions

View File

@@ -0,0 +1,35 @@
---
html: fees.html
parent: transactions.html
seo:
description: Learn about the types of fees allowed by the XRP Ledger, including neutral fees (payable to no one) that protect the ledger against abuse, as well as fees that users can collect from each other.
labels:
- Fees
---
# Fees (Disambiguation)
The XRP Ledger is a decentralized ledger, secured by cryptography and powered by a distributed peer-to-peer network of servers. This means that no one party, not even Ripple, can require a fee for access to the network.
However, the rules of the XRP Ledger include several types of fees, including neutral fees which protect the ledger against abuse. These neutral fees are not paid to anyone. There are also several optional ways that users can collect fees from each other, both inside and outside the XRP Ledger.
## In the Ledger
### Neutral Fees
The _**transaction cost**_ (sometimes called the transaction fee) is a miniscule amount of XRP destroyed to send a transaction. This cost scales with the load of the network, which protects the peer-to-peer network from spam. See [Transaction Cost](transaction-cost.md) for more information.
The _**reserve requirement**_ is a minimum amount of XRP that an account must hold. It increases with the number of objects the account owns in the ledger. This disincentivizes users from increasing the size of the ledger carelessly or maliciously. See [Reserves](../accounts/reserves.md) for more information.
### Optional Fees
_**Transfer fees**_ are optional percentage fees that issuers can charge to transfer the currencies they issue to other addresses within the XRP Ledger. See [Transfer Fees](../tokens/transfer-fees.md) for more information.
_**Trust line quality**_ is a setting that allows an account to value balances on a trust line at higher or lower than face value. This can lead to situations that are like charging a fee. Trust line quality does not apply to XRP, which is not tied to a trust line.
## Outside the Ledger
Although the fees described above are the only fees built into the XRP Ledger, people can still invent ways to charge fees associated with the ledger. For example, financial institutions commonly charge their customers to send money into and out of the XRP Ledger.
Many other fees are also possible. Businesses might charge for access to a client application, maintenance of non-XRP Ledger accounts, exchange services (especially when buying XRP on a private market instead of within the XRP Ledger's decentralized exchange) and any number of other services. Always be aware of the fee schedule before doing business with any financial institution.

View File

@@ -0,0 +1,25 @@
---
html: canceling-a-transaction.html
parent: finality-of-results.html
seo:
description: Understand when and how it's possible to cancel a transaction that has already been sent.
labels:
- Transaction Sending
---
# Canceling a Transaction
An important and intentional feature of the XRP Ledger is that a [transaction](../index.md)'s outcome is [final](index.md) as soon as it has been incorporated in a [ledger version](../../ledgers/index.md) that is validated by the [consensus process](../../consensus-protocol/index.md).
If a transaction has _not_ yet been included in a validated ledger, it may be possible to effectively cancel it by sending another transaction from the same sending address with the same `Sequence` value. If you do not want the replacement transaction to do anything, send an [AccountSet transaction][] with no options.
**Caution:** There is no guaranteed way to cancel a valid transaction after it has been distributed to the network. The process described here may or may not work depending on factors including how busy the network is, the network topology, and the [transaction cost](../transaction-cost.md) of the proposed transaction.
If the transaction has already been distributed to the network and proposed as a [candidate transaction](../../consensus-protocol/consensus-structure.md#consensus) in servers' consensus proposals, it may be too late to cancel. It is more likely that you can successfully cancel a transaction that is [queued](../transaction-queue.md) or is stuck "in limbo" because its [transaction cost](../transaction-cost.md) is not high enough to meet the network's current requirements. In this case, the replacement transaction can either do nothing, or do the same thing as the transaction to be canceled. The replacement transaction is more likely to succeed if its transaction cost is higher.
For example, if you try to submit 3 transactions with sequence numbers 11, 12, and 13, but transaction 11 gets lost somehow or does not have a high enough [transaction cost](../transaction-cost.md) to be propagated to the network, then you can cancel transaction 11 by submitting an AccountSet transaction with no options and sequence number 11. This does nothing (except destroying the transaction cost for the new transaction 11), but it allows transactions 12 and 13 to become valid.
This approach is preferable to renumbering and resubmitting transactions 12 and 13, because it prevents transactions from being effectively duplicated under different sequence numbers.
In this way, an AccountSet transaction with no options is the canonical "[no-op](http://en.wikipedia.org/wiki/NOP)" transaction.
{% raw-partial file="/_snippets/common-links.md" /%}

View File

@@ -0,0 +1,60 @@
---
html: finality-of-results.html
parent: transactions.html
seo:
description: Learn when the outcome of a transaction is final and immutable.
labels:
- Transaction Sending
- Blockchain
---
# Finality of Results
The order in which transactions apply to the consensus [ledger](../../ledgers/index.md) is not final until a ledger is closed and the exact transaction set is approved by the [consensus process](../../consensus-protocol/index.md). A transaction that succeeded initially could still fail, and a transaction that failed initially could still succeed. Additionally, a transaction that was rejected by the consensus process in one round could achieve consensus in a later round.
A validated ledger can include successful transactions (`tes` result codes) as well as failed transactions (`tec` result codes). No transaction with any other result is included in a ledger.
For any other result code, it can be difficult to determine if the result is final. The following table summarizes when a transaction's outcome is final, based on the result code from submitting the transaction:
| Result Code | Finality |
|:----------------|:-----------------------------------------------------------|
| `tesSUCCESS` | Final when included in a validated ledger |
| Any `tec` code | Final when included in a validated ledger |
| Any `tem` code | Final unless the protocol changes to make the transaction valid |
| `tefPAST_SEQ` | Final when another transaction with the same sequence number is included in a validated ledger |
| `tefMAX_LEDGER` | Final when a validated ledger has a [ledger index][Ledger Index] higher than the transaction's `LastLedgerSequence` field, and no validated ledger includes the transaction |
Any other transaction result is potentially not final. In that case, the transaction could still succeed or fail later, especially if conditions change such that the transaction is no longer prevented from applying. For example, trying to send a non-XRP currency to an account that does not exist yet would fail, but it could succeed if another transaction sends enough XRP to create the destination account. A server might even store a temporarily-failed, signed transaction and then successfully apply it later without asking first.
## How can non-final results change?
When you initially submit a transaction, the `rippled` server tentatively applies that transaction to its current open ledger, then returns the tentative [transaction results](../../../references/protocol/transactions/transaction-results/transaction-results.md) from doing so. However, the transaction's final result may be very different than its tentative results, for several reasons:
- The transaction may be delayed until a later ledger version, or may never be included in a validated ledger. For the most part, the XRP Ledger follows a principle that all valid transactions should be processed as soon as possible. However, there are exceptions, including:
- If a proposed transaction has not been relayed to a majority of validators by the time a [consensus round](../../consensus-protocol/index.md) begins, it may be postponed until the next ledger version, to give the remaining validators time to fetch the transaction and confirm that it is valid.
- If an address sends two different transactions using the same sequence number, at most one of those transactions can become validated. If those transactions are relayed through the network in different paths, a tentatively-successful transaction that some servers saw first may end up failing because the other, conflicting transaction reached a majority of servers first.
- To protect the network from spam, all transactions must destroy a [transaction cost](../transaction-cost.md) in XRP to be relayed throughout the XRP Ledger peer-to-peer network. If heavy load on the peer-to-peer network causes the transaction cost to increase, a transaction that tentatively succeeded may not get relayed to enough servers to achieve a consensus, or may be [queued](../transaction-queue.md) for later.
- Temporary internet outages or delays may prevent a proposed transaction from being successfully relayed before the transaction's intended expiration, as set by the `LastLedgerSequence` field. (If the transaction does not have an expiration, then it remains valid and could succeed any amount of time later, which can be undesirable in its own way. See [Reliable Transaction Submission](../reliable-transaction-submission.md) for details.)
- Combinations of two or more of these factors can also occur.
- The [order transactions apply in a closed ledger](../../ledgers/open-closed-validated-ledgers.md) is usually different than the order those transactions were tentatively applied to a current open ledger; depending on the transactions involved, this can cause a tentatively-successful transaction to fail or a tentatively-failed transaction to succeed. Some examples include:
- If two transactions would each fully consume the same Offer in the [decentralized exchange](../../tokens/decentralized-exchange/index.md), whichever one comes first succeeds, and the other fails. Since the order in which those transactions apply may change, the one that succeeded can fail and the one that failed can succeed. Since oOffers can be partially executed, they could also still succeed, but to a greater or lesser extent.
- If a [cross-currency payment](../../payment-types/cross-currency-payments.md) succeeds by consuming an Offer in the decentralized exchange, but a different transaction consumes or creates offers in the same order book, the cross-currency payment may succeed with a different exchange rate than it had when it executed tentatively. If it was a [partial payment](../../payment-types/partial-payments.md), it could also deliver a different amount.
- A [Payment transaction][] that tentatively failed because the sender did not have enough funds may later succeed because another transaction delivering the necessary funds came first in the canonical order. The reverse is also possible: a transaction that tentatively succeeded may fail because a transaction delivering the necessary funds did not come first after being put into canonical order.
**Tip:** For this reason, when running tests against the XRP Ledger, be sure to wait for a ledger close in between transactions if you have several accounts affecting the same data. If you are testing against a server in [stand-alone mode][], you must [manually close the ledger](../../../infrastructure/testing-and-auditing/advance-the-ledger-in-stand-alone-mode.md) in such cases.
## See Also
- [Look up Transaction Results](look-up-transaction-results.md)
- [Transaction Results Reference](../../../references/protocol/transactions/transaction-results/transaction-results.md)
{% raw-partial file="/_snippets/common-links.md" /%}

View File

@@ -0,0 +1,455 @@
---
html: look-up-transaction-results.html
parent: finality-of-results.html
seo:
description: Find the results of previously-submitted transactions.
labels:
- Payments
- Development
---
# Look Up Transaction Results
To use the XRP Ledger effectively, you need to be able to understand [transaction](../index.md) outcomes: did the transaction succeed? What did it do? If it failed, why?
The XRP Ledger is a shared system, with all data recorded publicly and carefully, securely updated with each new [ledger version](../../ledgers/index.md). Anyone can look up the exact outcome of any transaction and read the [transaction metadata](../../../references/protocol/transactions/metadata.md) to see what it did.
This document describes, at a low level, how to know why a transaction reached the outcome it did. For an end-user, it is easier to look at a processed view of a transaction. For example, you can [use XRP Charts to get an English-language description of any recorded transaction](https://xrpcharts.ripple.com/#/transactions/).
## Prerequisites
To understand the outcome of a transaction as described in these instructions, you must:
- Know which transaction you want to understand. If you know the transaction's [identifying hash][], you can look it up that way. You can also look at transactions that executed in a recent ledger or the transactions that most recently affected a given account.
- Have access to a `rippled` server that provides reliable information and has the necessary history for when the transaction was submitted.
- For looking up the outcomes of transactions you've recently submitted, the server you submitted through should be enough, as long as it maintains sync with the network during that time.
- For outcomes of older transactions, you may want to use a [full-history server](../../networks-and-servers/ledger-history.md#full-history).
**Tip:** There are other ways of querying for data on transactions from the XRP Ledger, including the [Data API](../../../references/data-api.md) and other exported databases, but those interfaces are non-authoritative. This document describes how to look up data using the `rippled` API directly, for the most direct and authoritative results possible.
## 1. Get Transaction Status
Knowing whether a transaction succeeded or failed is a two-part question:
1. Was the transaction included in a validated ledger?
2. If so, what changes to the ledger state occurred as a result?
To know whether a transaction was included in a validated ledger, you usually need access to all the ledgers it could possibly be in. The simplest, most foolproof way to do this is to look up the transaction on a [full history server](../../networks-and-servers/ledger-history.md#full-history). Use the [tx method][], [account_tx method][], or other response from `rippled`. Look for `"validated": true` to indicate that this response uses a ledger version that has been validated by consensus.
- If the result does not have `"validated": true`, then the result may be tentative and you must wait for the ledger to be validated to know if the transaction's outcome is final.
- If the result does not contain the transaction in question, or returns the error `txnNotFound`, then the transaction is not in any ledger that the server has in its available history. This may or may not mean that the transaction failed, depending on whether the transaction could be in a validated ledger version that the server does not have and whether it could be included in a future validated ledger. You can constrain the range of ledgers a transaction can be in by knowing:
- The earliest ledger the transaction could be in, which is the **first ledger to be validated _after_ the transaction was first submitted**.
- The last ledger the transaction could be in, which is defined by the transaction's `LastLedgerSequence` field.
The following example shows a successful transaction, as returned by the [tx method][], which is in a validated ledger version. The order of the fields in the JSON response has been rearranged, with some parts omitted, to make it easier to understand:
```json
{
"TransactionType": "AccountSet",
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Sequence": 376,
"hash": "017DED8F5E20F0335C6F56E3D5EE7EF5F7E83FB81D2904072E665EEA69402567",
... (omitted) ...
"meta": {
"AffectedNodes": [
... (omitted) ...
],
"TransactionResult": "tesSUCCESS"
},
"ledger_index": 46447423,
"validated": true
}
```
This example shows an [AccountSet transaction][] sent by the [account](../../accounts/accounts.md) with address `rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn`, using [Sequence number][] 376. The transaction's [identifying hash][] is `017DED8F5E20F0335C6F56E3D5EE7EF5F7E83FB81D2904072E665EEA69402567` and its [result](../../../references/protocol/transactions/transaction-results/transaction-results.md) is `tesSUCCESS`. The transaction was included in ledger version 46447423, which has been validated, so these results are final.
### Case: Not Included in a Validated Ledger
**If a transaction is not included in a validated ledger, it cannot possibly have had _any_ effect on the shared XRP Ledger state.** If the transaction's failure to be included in a ledger is [_final_](index.md), then it cannot have any future effect, either.
If the transaction's failure is not final, it may still become included in a _future_ validated ledger. You can use the provisional results of applying the transaction to the current open ledger as a preview of the likely effects the transaction may have in a final ledger, but those results can change due to [many factors](index.md#how-can-non-final-results-change).
### Case: Included in a Validated Ledger
If the transaction _is_ included in a validated ledger, then the [transaction metadata](../../../references/protocol/transactions/metadata.md) contains a full report of all changes that were made to the ledger state as a result of processing the transaction. The metadata's `TransactionResult` field contains a [transaction result code](../../../references/protocol/transactions/transaction-results/transaction-results.md) that summarizes the outcome:
- The code `tesSUCCESS` indicates that the transaction was, more or less, successful.
- A `tec`-class code indicates that the transaction failed, and its only effects on the ledger state are to destroy the XRP [transaction cost](../transaction-cost.md) and possibly perform some bookkeeping like removing [expired Offers](../../tokens/decentralized-exchange/offers.md#offer-expiration) and [closed payment channels](../../payment-types/payment-channels.md#payment-channel-lifecycle).
- No other code can appear in any ledger.
The result code is only a summary of the transaction's outcome. To understand in more detail what the transaction did, you must read the rest of the metadata in context of the transaction's instructions and the ledger state before the transaction executed.
## 2. Interpret Metadata
Transaction metadata describes _exactly_ how the transaction was applied to the ledger, including the following fields:
{% partial file="/_snippets/tx-metadata-field-table.md" /%}
Most of the metadata is contained in [the `AffectedNodes` array](../../../references/protocol/transactions/metadata.md#affectednodes). What to look for in this array depends on the type of transaction. Almost every transaction modifies the sender's [AccountRoot object][] to destroy the XRP [transaction cost](../transaction-cost.md) and increase the [account's Sequence number](../../../references/protocol/data-types/basic-data-types.md#account-sequence).
**Info:** One exception to this rule is for [pseudo-transactions](../../../references/protocol/transactions/pseudo-transaction-types/pseudo-transaction-types.md), which aren't sent from a real account and thus do not modify an AccountRoot object. There are other exceptions that modify an AccountRoot object without changing its `Balance` field: [free key reset transactions](../transaction-cost.md#key-reset-transaction) do not change the sender's XRP balance; and in the unlikely scenario that a transaction causes an account to receive exactly as much XRP as it destroys, the account's Balance shows no net change. (The net decrease in XRP occurs elsewhere in the metadata, debited from wherever the account sent the XRP.)
This example shows the full response from step 1 above. See if you can figure out what changes it made to the ledger:
```json
{
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "12",
"Flags": 2147483648,
"LastLedgerSequence": 46447424,
"Sequence": 376,
"SigningPubKey": "03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB",
"TransactionType": "AccountSet",
"TxnSignature": "30450221009B2910D34527F4EA1A02C375D5C38CF768386ACDE0D17CDB04C564EC819D6A2C022064F419272003AA151BB32424F42FC3DBE060C8835031A4B79B69B0275247D5F4",
"date": 608257201,
"hash": "017DED8F5E20F0335C6F56E3D5EE7EF5F7E83FB81D2904072E665EEA69402567",
"inLedger": 46447423,
"ledger_index": 46447423,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
"FinalFields": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"AccountTxnID": "017DED8F5E20F0335C6F56E3D5EE7EF5F7E83FB81D2904072E665EEA69402567",
"Balance": "396015164",
"Domain": "6D64756F31332E636F6D",
"EmailHash": "98B4375E1D753E5B91627516F6D70977",
"Flags": 8519680,
"MessageKey": "0000000000000000000000070000000300",
"OwnerCount": 9,
"Sequence": 377,
"TransferRate": 4294967295
},
"PreviousFields": {
"AccountTxnID": "E710CADE7FE9C26C51E8630138322D80926BE91E46D69BF2F36E6E4598D6D0CF",
"Balance": "396015176",
"Sequence": 376
},
"PreviousTxnID": "E710CADE7FE9C26C51E8630138322D80926BE91E46D69BF2F36E6E4598D6D0CF",
"PreviousTxnLgrSeq": 46447387
}
}
],
"TransactionIndex": 13,
"TransactionResult": "tesSUCCESS"
},
"validated": true
}
```
The _only_ changes made by this [no-op transaction](canceling-a-transaction.md) are to update the [AccountRoot object][] representing the sender's account in the following ways:
- The `Sequence` value increases from 376 to 377.
- The XRP `Balance` in this account changes from `396015176` to `396015164` [drops of XRP][]. This decrease of exactly 12 drops represents the [transaction cost](../transaction-cost.md), as specified in the `Fee` field of the transaction.
- The [`AccountTxnID`](../../../references/protocol/transactions/common-fields.md#accounttxnid) changes to show that this transaction is now the one most recently sent from this address.
- The previous transaction to affect this account was the transaction `E710CADE7FE9C26C51E8630138322D80926BE91E46D69BF2F36E6E4598D6D0CF`, which executed in ledger version 46447387, as specified in the `PreviousTxnID` and `PreviousTxnLgrSeq` fields. (This may be useful if you want to walk backwards through the account's transaction history.)
**Note:** Although the metadata does not explicitly show it, any time a transaction modifies a ledger object, it updates that object's `PreviousTxnID` and `PreviousTxnLgrSeq` fields with the current transaction's information. If the same sender has multiple transactions in a single ledger version, each one after the first provides a `PreviousTxnLgrSeq` whose value is the [ledger index](../../../references/protocol/data-types/basic-data-types.md#ledger-index) of the ledger version that included all those transactions.
Since the `ModifiedNode` entry for `rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn`'s account is the only object in the `AffectedNodes` array, no other changes were made to the ledger as a result of this transaction.
**Tip:** If the transaction sends or receives XRP, the sender's balance changes are combined with the transaction cost, resulting in a single change to the `Balance` field in the net amount. For example, if you sent 1 XRP (1,000,000 drops) and destroyed 10 drops for the transaction cost, the metadata shows your `Balance` decreasing by 1,000,010 drops of XRP.
### General-Purpose Bookkeeping
Almost any transaction can result in the following types of changes:
- **Sequence and Transaction Cost changes:** [As mentioned, every transaction (excluding pseudo-transactions) modifies the sender's `AccountRoot` object](#2-interpret-metadata) to increase the sender's sequence number and destroy the XRP used to pay the transaction cost.
- **Account Threading:** Some transactions that create objects also modify the [AccountRoot object](../../../references/protocol/ledger-data/ledger-entry-types/accountroot.md) of an intended recipient or destination account to indicate that something relating to that account changed. This technique of "tagging" an account changes only that object's `PreviousTxnID` and `PreviousTxnLgrSeq` fields. This makes it more efficient to look up an account's transaction history by following the "thread" of transactions mentioned in these fields.
- **Directory Updates:** Transactions that create or remove ledger objects often make changes to [DirectoryNode objects](../../../references/protocol/ledger-data/ledger-entry-types/directorynode.md) to track which objects exist. Also, when a transaction adds an object that counts towards an account's [owner reserve](../../accounts/reserves.md#owner-reserves), it increases the `OwnerCount` of the owner's [AccountRoot object][]. Removing an object decreases the `OwnerCount`. This is how the XRP Ledger tracks how much owner reserve each account owes at any point in time.
Example of increasing an Account's `OwnerCount`:
```json
{
"ModifiedNode": {
"FinalFields": {
"Account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"Balance": "9999999990",
"Flags": 0,
"OwnerCount": 1,
"Sequence": 2
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05",
"PreviousFields": {
"Balance": "10000000000",
"OwnerCount": 0,
"Sequence": 1
},
"PreviousTxnID": "B24159F8552C355D35E43623F0E5AD965ADBF034D482421529E2703904E1EC09",
"PreviousTxnLgrSeq": 16154
}
}
```
Many transaction types create or modify [DirectoryNode objects](../../../references/protocol/ledger-data/ledger-entry-types/directorynode.md). These objects are for bookkeeping: tracking all objects attached to an account, or all exchange Offers at the same exchange rate. If the transaction created new objects in the ledger, it may need to add entries to an existing DirectoryNode object, or add another DirectoryNode object to represent another page of the directory. If the transaction removed objects from the ledger, it may delete one or more DirectoryNode objects that are no longer needed.
Example of a `CreatedNode` representing a new Offer Directory:
```json
{
"CreatedNode": {
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "F60ADF645E78B69857D2E4AEC8B7742FEABC8431BD8611D099B428C3E816DF93",
"NewFields": {
"ExchangeRate": "4E11C37937E08000",
"RootIndex": "F60ADF645E78B69857D2E4AEC8B7742FEABC8431BD8611D099B428C3E816DF93",
"TakerPaysCurrency": "0000000000000000000000004254430000000000",
"TakerPaysIssuer": "5E7B112523F68D2F5E879DB4EAC51C6698A69304"
}
}
},
```
Other things to look for when processing transaction metadata depend on the transaction type.
### Payments
A [Payment transaction][] can represent a direct XRP-to-XRP transaction, a [cross-currency payment](../../payment-types/cross-currency-payments.md), or a direct transaction of a fungible [token](../../tokens/index.md). Anything other than a direct XRP-to-XRP transaction can be a [partial payment](../../payment-types/partial-payments.md), including token-to-XRP or XRP-to-token transactions.
XRP amounts are tracked in the `Balance` field of `AccountRoot` objects. (XRP can also exist in [Escrow objects](../../../references/protocol/ledger-data/ledger-entry-types/escrow.md) and [PayChannel objects](../../../references/protocol/ledger-data/ledger-entry-types/paychannel.md), but Payment transactions cannot affect those.)
You should always use [the `delivered_amount` field](../../payment-types/partial-payments.md#the-delivered_amount-field) to see how much a payment delivered.
If the payment contains a `CreatedNode` with `"LedgerEntryType": "AccountRoot"`, that means the payment [funded a new account](../../accounts/accounts.md#creating-accounts) in the ledger.
#### Token Payments
Payments involving tokens are a bit more complicated.
All changes in token balances are reflected in [RippleState objects](../../../references/protocol/ledger-data/ledger-entry-types/ripplestate.md), which represent [trust lines](../../tokens/fungible-tokens/index.md). An increase to one party's balance on a trust line is considered to decrease the counterparty's balance by equal amount; in the metadata, this is only recorded as a single change to the shared `Balance` for the RippleState object. Whether this change is recorded as an "increase" or "decrease" depends on which account has the numerically higher address.
A single payment may go across a long [path](../../tokens/fungible-tokens/paths.md) consisting of several trust lines and order books. The process of changing the balances on several trust lines to connect parties indirectly is called [rippling](../../tokens/fungible-tokens/rippling.md). Depending on the `issuer` specified in the transaction's `Amount` field, it is also possible that the amount delivered may be split between several trust lines (`RippleState` accounts) connected to the destination account.
**Tip:** The order that modified objects are presented in the metadata does not necessarily match the order those objects were visited while processing a payment. To better understand payment execution, it may help to reorder `AffectedNodes` members to reconstruct the paths the funds took through the ledger.
Cross-currency payments consume [Offers](../../../references/protocol/ledger-data/ledger-entry-types/offer.md) in part or entirely to change between different currency codes and issuers. If a transaction shows `DeletedNode` objects for `Offer` types, that can indicate an Offer that was fully consumed, or an Offer that was found to be [expired or unfunded](../../tokens/decentralized-exchange/offers.md#lifecycle-of-an-offer) at the time of processing. If a transaction shows a `ModifiedNode` of type `Offer`, that indicates an Offer that was partially consumed.
The [`QualityIn` and `QualityOut` settings of trust lines](../../../references/protocol/transactions/types/trustset.md) can affect how one side of a trust line values the token, so that the numeric change in balances is different from how the sender values that token. The `delivered_amount` shows how much was delivered as valued by the recipient.
If the amount to be sent or received is outside of the [token precision](../../../references/protocol/data-types/currency-formats.md#token-precision), it is possible that one side may be debited for an amount that is rounded to nothing on the other side of the transaction. As a result, when two parties transact while their balances are different by a factor of 10<sup>16</sup>, it is possible that rounding may effectively "create" or "destroy" small amounts of the token. (XRP is never rounded, so this is not possible with XRP.)
Depending on the length of the [paths](../../tokens/fungible-tokens/paths.md), the metadata for cross-currency payments can be _long_. For example, [transaction 8C55AFC2A2AA42B5CE624AEECDB3ACFDD1E5379D4E5BF74A8460C5E97EF8706B](https://xrpcharts.ripple.com/#/transactions/8C55AFC2A2AA42B5CE624AEECDB3ACFDD1E5379D4E5BF74A8460C5E97EF8706B) delivered 2.788 GCB issued by `rHaaans...`, spending XRP but passing through USD from 2 issuers, paying XRP to 2 accounts, removing an unfunded offer from `r9ZoLsJ...` to trade EUR for ETH, plus bookkeeping for a total of 17 different ledger objects modified. <!-- SPELLING_IGNORE: gcb -->
### Offers
An [OfferCreate transaction][] may or may not create an object in the ledger, depending on how much was matched and whether the transaction used flags such as `tfImmediateOrCancel`. Look for a `CreatedNode` entry with `"LedgerEntryType": "Offer"` to see if the transaction added a new Offer to the ledger's order books. For example:
```json
{
"CreatedNode": {
"LedgerEntryType": "Offer",
"LedgerIndex": "F39B13FA15AD2A345A9613934AB3B5D94828D6457CCBB51E3135B6C44AE4BC83",
"NewFields": {
"Account": "rETSmijMPXT9fnDbLADZnecxgkoJJ6iKUA",
"BookDirectory": "CA462483C85A90DB76D8903681442394D8A5E2D0FFAC259C5B0C59269BFDDB2E",
"Expiration": 608427156,
"Sequence": 1082535,
"TakerGets": {
"currency": "EUR",
"issuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq",
"value": "2157.825"
},
"TakerPays": "7500000000"
}
}
}
```
A `ModifiedNode` of type `Offer` indicates an Offer that was matched and partially consumed. A single transaction can consume a large number of Offers. An Offer to trade two tokens might also consume Offers to trade XRP because of [auto-bridging](../../tokens/decentralized-exchange/autobridging.md). All or part of an exchange can be auto-bridged.
A `DeletedNode` of type `Offer` can indicate a matching Offer that was fully consumed, an Offer that was found to be [expired or unfunded](../../tokens/decentralized-exchange/offers.md#lifecycle-of-an-offer) at the time of processing, or an Offer that was canceled as part of placing a new Offer. You can recognize a canceled Offer because the `Account` that placed it is the sender of the transaction that deleted it.
Example of a deleted Offer:
```json
{
"DeletedNode": {
"FinalFields": {
"Account": "rETSmijMPXT9fnDbLADZnecxgkoJJ6iKUA",
"BookDirectory": "CA462483C85A90DB76D8903681442394D8A5E2D0FFAC259C5B0C595EDE3E1EE9",
"BookNode": "0000000000000000",
"Expiration": 608427144,
"Flags": 0,
"OwnerNode": "0000000000000000",
"PreviousTxnID": "0CA50181C1C2A4D45E9745F69B33FA0D34E60D4636562B9D9CDA1D4E2EFD1823",
"PreviousTxnLgrSeq": 46493676,
"Sequence": 1082533,
"TakerGets": {
"currency": "EUR",
"issuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq",
"value": "2157.675"
},
"TakerPays": "7500000000"
},
"LedgerEntryType": "Offer",
"LedgerIndex": "9DC99BF87F22FB957C86EE6D48407201C87FBE623B2F1BC4B950F83752B55E27"
}
}
```
Offers can create, delete, and modify both types of [DirectoryNode objects](../../../references/protocol/ledger-data/ledger-entry-types/directorynode.md), to keep track of who placed which Offers and which Offers are available at which exchange rates. Generally, users don't need to pay close attention to this bookkeeping.
An [OfferCancel transaction][] may have the code `tesSUCCESS` even if there was no Offer to delete. Look for a `DeletedNode` of type `Offer` to confirm that the transaction actually deleted an Offer. If not, the Offer may already have been removed by a previous transaction, or the OfferCancel transaction may have used the wrong sequence number in the `OfferSequence` field.
If an OfferCreate transaction shows a `CreatedNode` of type `RippleState`, that indicates that [the Offer created a trust line](../../tokens/decentralized-exchange/offers.md#offers-and-trust) to hold a token received in the trade.
### Escrows
A successful [EscrowCreate transaction][] creates an [Escrow object](../../../references/protocol/ledger-data/ledger-entry-types/escrow.md) in the ledger. Look for a `CreatedNode` entry of type `Escrow`. The `NewFields` should show an `Amount` equal to the amount of XRP escrowed, and other properties as specified.
A successful EscrowCreate transaction also debits the same amount of XRP from the sender. Look for a `ModifiedNode` of type `AccountRoot`, where the `Account` in the final fields matches the address from the `Account` in the transaction instructions. The `Balance` should show the decrease in XRP due to the escrowed XRP (and the XRP destroyed to pay the transaction cost).
A successful [EscrowFinish transaction][] modifies the `AccountRoot` of the recipient to increase their XRP balance (in the `Balance` field), deletes the `Escrow` object, and reduces the owner count of the escrow creator. Since the escrow's creator, recipient, and finisher may all be different accounts or the same, this can result in _one to three_ `ModifiedNode` objects of type `AccountRoot`. A successful [EscrowCancel transaction][] is very similar, except it sends the XRP back to the original creator of the escrow.
Of course, an EscrowFinish can only be successful if it meets the conditions of the escrow, and an EscrowCancel can only be successful if the expiration of the Escrow object is before the close time of the previous ledger.
Escrow transactions also do normal [bookkeeping](#general-purpose-bookkeeping) for adjusting the sender's owner reserve and the directories of the accounts involved.
In the following excerpt, we see that `r9UUEX...`'s balance increases by 1 billion XRP and its owner count decreases by 1 because an escrow from that account to itself finished successfully. The `Sequence` number does not change because [a third party completed the escrow](https://xrpcharts.ripple.com/#/transactions/C4FE7F5643E20E7C761D92A1B8C98320614DD8B8CD8A04CFD990EBC5A39DDEA2):
```json
{
"ModifiedNode": {
"FinalFields": {
"Account": "r9UUEXn3cx2seufBkDa8F86usfjWM6HiYp",
"Balance": "1650000199898000",
"Flags": 1048576,
"OwnerCount": 11,
"Sequence": 23
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "13FDBC39E87D9B02F50940F9FDDDBFF825050B05BE7BE09C98FB05E49DD53FCA",
"PreviousFields": {
"Balance": "650000199898000",
"OwnerCount": 12
},
"PreviousTxnID": "D853342BC27D8F548CE4D7CB688A8FECE3229177790453BA80BC79DE9AAC3316",
"PreviousTxnLgrSeq": 41005507
}
},
{
"DeletedNode": {
"FinalFields": {
"Account": "r9UUEXn3cx2seufBkDa8F86usfjWM6HiYp",
"Amount": "1000000000000000",
"Destination": "r9UUEXn3cx2seufBkDa8F86usfjWM6HiYp",
"FinishAfter": 589075200,
"Flags": 0,
"OwnerNode": "0000000000000000",
"PreviousTxnID": "D5FB1C7D18F931A4FBFA468606220560C17ADF6DE230DA549F4BD11A81F19DFC",
"PreviousTxnLgrSeq": 35059548
},
"LedgerEntryType": "Escrow",
"LedgerIndex": "62F0ABB58C874A443F01CDCCA18B12E6DA69C254D3FB17A8B71CD8C6C68DB74D"
}
},
```
### Payment Channels
Look for a `CreatedNode` of type `PayChannel` when creating a payment channel. You should also find a `ModifiedNode` of type `AccountRoot` showing the decrease in the sender's balance. Look for an `Account` field in the `FinalFields` to confirm that the address matches the sender, and look at the difference in the `Balance` fields to see the change in XRP balance.
The metadata also lists the newly-created payment channel in the destination's [owner directory](../../../references/protocol/ledger-data/ledger-entry-types/directorynode.md). This prevents an account from [being deleted](../../accounts/deleting-accounts.md) if it is the destination of an open payment channel. (This behavior was added by the [fixPayChanRecipientOwnerDir amendment](../../../resources/known-amendments.md#fixpaychanrecipientownerdir).)
There are several ways to request to close a payment channel, aside from the immutable `CancelAfter` time of the channel (which is only set on creation). If a transaction schedules a channel to close, there is a `ModifiedNode` entry of type `PayChannel` for the channel, with the newly-added close time in the `Expiration` field of the `FinalFields`. The following example shows the changes to a `PayChannel` in a case where the sender requested to close the channel without redeeming a claim:
```json
{
"ModifiedNode": {
"FinalFields": {
"Account": "rNn78XpaTXpgLPGNcLwAmrcS8FifRWMWB6",
"Amount": "1000000",
"Balance": "0",
"Destination": "rwWfYsWiKRhYSkLtm3Aad48MMqotjPkU1F",
"Expiration": 608432060,
"Flags": 0,
"OwnerNode": "0000000000000002",
"PublicKey": "EDEACA57575C6824FC844B1DB4BF4AF2B01F3602F6A9AD9CFB8A3E47E2FD23683B",
"SettleDelay": 3600,
"SourceTag": 1613739140
},
"LedgerEntryType": "PayChannel",
"LedgerIndex": "DC99821FAF6345A4A6C41D5BEE402A7EA9198550F08D59512A69BFC069DC9778",
"PreviousFields": {},
"PreviousTxnID": "A9D6469F3CB233795B330CC8A73D08C44B4723EFEE11426FEE8E7CECC611E18E",
"PreviousTxnLgrSeq": 41889092
}
}
```
### TrustSet Transactions
TrustSet transactions create, modify, or delete [trust lines](../../tokens/fungible-tokens/index.md), which are represented as [`RippleState` objects](../../../references/protocol/ledger-data/ledger-entry-types/ripplestate.md). A single `RippleState` object contains settings for both parties involved, including their limits, [rippling settings](../../tokens/fungible-tokens/rippling.md), and more. Creating and modifying trust lines can also [adjust the sender's owner reserve and owner directory](#general-purpose-bookkeeping).
The following example shows a new trust line, where **`rf1BiG...`** is willing to hold up to 110 USD issued by **`rsA2Lp...`**:
```json
{
"CreatedNode": {
"LedgerEntryType": "RippleState",
"LedgerIndex": "9CA88CDEDFF9252B3DE183CE35B038F57282BC9503CDFA1923EF9A95DF0D6F7B",
"NewFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value": "110"
},
"LowLimit": {
"currency": "USD",
"issuer": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"value": "0"
}
}
}
}
```
### Other Transactions
Most other transactions create a specific type of ledger entry and [adjust the sender's owner reserve and owner directory](#general-purpose-bookkeeping):
- [AccountSet transactions][] modify the sender's existing [AccountRoot object][], changing the settings and flags as specified.
- [DepositPreauth transactions][] add or remove a [DepositPreauth object](../../../references/protocol/ledger-data/ledger-entry-types/depositpreauth.md) for a specific sender.
- [SetRegularKey transactions][] modify the [AccountRoot object][] of the sender, changing the `RegularKey` field as specified.
- [SignerListSet transactions][] add, remove, or replace a [SignerList object](../../../references/protocol/ledger-data/ledger-entry-types/signerlist.md).
### Pseudo-Transactions
[Pseudo-transactions](../../../references/protocol/transactions/pseudo-transaction-types/pseudo-transaction-types.md) also have metadata, but they do not follow all the rules of normal transactions. They are not tied to a real account (the `Account` value is the [base58-encoded form of the number 0](../../accounts/addresses.md#special-addresses)), so they do not modify an AccountRoot object in the ledger to increase the `Sequence` number or destroy XRP. Pseudo-transactions only make specific changes to special ledger objects:
- [EnableAmendment pseudo-transactions][] modify the [Amendments ledger object](../../../references/protocol/ledger-data/ledger-entry-types/amendments.md) to track which amendments are enabled, and which ones are pending with majority support and for how long.
- [SetFee pseudo-transactions][] modify the [FeeSettings ledger object](../../../references/protocol/ledger-data/ledger-entry-types/feesettings.md) to change the base levels for the [transaction cost](../transaction-cost.md) and [reserve requirements](../../accounts/reserves.md).
## See Also
- **Concepts:**
- [Finality of Results](index.md)
- [Reliable Transaction Submission](../reliable-transaction-submission.md)
- **Tutorials:**
- [Monitor Incoming Payments with WebSocket](../../../tutorials/get-started/monitor-incoming-payments-with-websocket.md)
- **References:**
- [Ledger Entry Types Reference](../../../references/protocol/ledger-data/ledger-entry-types/index.md) - All possible fields of all types of ledger entries
- [Transaction Metadata](../../../references/protocol/transactions/metadata.md) - Summary of the metadata format and fields that appear in metadata
- [Transaction Results](../../../references/protocol/transactions/transaction-results/transaction-results.md) - Tables of all possible result codes for transactions.
{% raw-partial file="/_snippets/common-links.md" /%}

View File

@@ -0,0 +1,149 @@
---
html: transaction-malleability.html
parent: finality-of-results.html
seo:
description: Be aware of ways transactions could be changed to have a different hash than expected.
labels:
- Security
- Transaction Sending
---
# Transaction Malleability
A transaction is "malleable" if it can be changed in any way after being signed, without the keys to sign it. In the XRP Ledger, the **functionality** of a signed transaction cannot change, but in some circumstances a third party _could_ change the signature and identifying hash of a transaction.
If vulnerable software submits malleable transactions and assumes they can only execute under the original hash, it may lose track of transactions. In the worst case, malicious actors could take advantage of this to steal money from the vulnerable system.
On the XRP Ledger mainnet, only **multi-signed transactions** can be malleable, if they have more signatures than necessary, or if an authorized signer provides an additional signature beyond what is necessary. Good operational security can protect against these problems. See [Mitigations for Multi-Signature Malleability](#mitigations-for-multi-signature-malleability) for guidelines.
Before 2014, single-signed transactions could be malleable due to properties of the default signing algorithm, ECDSA with the secp256k1 curve. For compatibility with legacy signing tools, it was possible to create and submit malleable single-signed transactions until the [RequireFullyCanonicalSig amendment][] became enabled on 2020-07-03. (Transactions [signed with Ed25519 keys](../../accounts/cryptographic-keys.md#signing-algorithms) were never vulnerable to this problem.)
## Background
In the XRP Ledger, a transaction cannot execute unless:
- All [fields of a transaction](../../../references/protocol/transactions/common-fields.md) are signed, except the signature itself.
- The key pair(s) used to sign the transaction are [authorized to send transactions on behalf of that account](../index.md#authorizing-transactions).
- The signature is _canonical_ and matches the transaction instructions.
Any change to the signed fields, no matter how small, would invalidate the signature, so no part of the transaction can be malleable except for the signature itself. In most cases, any change to a signature itself also invalidates the signature, but there are some specific exceptions, described below.
Since the signature is among the data used to compute a transaction's identifying hash, any changes to a malleable transaction result in a different hash.
### Alternate secp256k1 Signatures
To be "canonical", signatures created with the ECDSA algorithm and secp256k1 curve (the default) must meet the following requirements:
- The signature must be properly [DER-encoded data](https://en.wikipedia.org/wiki/X.690#DER_encoding).
- The signature must not have any padding bytes outside the DER-encoded data.
- The signature's component integers must not be negative, and they must not be larger than the secp256k1 group order. <!-- STYLE_OVERRIDE: component -->
Generally speaking, any standard ECDSA implementation handles these requirements automatically. However, with secp256k1, those requirements are insufficient to prevent malleability. Thus, the XRP Ledger has a concept of "fully canonical" signatures which do not have the same problem.
An ECDSA signature consists of two integers, called R and S. The secp256k1 _group order_, called N, is a constant value for all secp256k1 signatures. Specifically, N is the value `0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141`. For any given signature `(R,S)`, the signature `(R, N-S)` (that is, using N minus S in place of S) is also valid.
Thus, to have _fully_ canonical signatures, one must choose which of the two possibilities is preferred and declare the other to be invalid. The creators of the XRP Ledger decided arbitrarily to prefer the _smaller_ of the two possible values, `S` or `N-S`. A transaction is considered _fully canonical_ if it uses the preferred (smaller) value of `S`, and follows all the normal rules for being canonical. To calculate a fully-canonical ECDSA signature, one must compare S and N-S to determine which is smaller, then use that value in the `Signature` field of the transaction.
With the [RequireFullyCanonicalSig amendment][] (enabled in 2020), all transactions must use _fully canonical_ signatures only.
Between 2014 and 2020, the XRP Ledger was compatible with legacy software that did not always generate fully canonical signatures, but used a flag on transactions called [**`tfFullyCanonicalSig`**](../../../references/protocol/transactions/common-fields.md#global-flags) to protect compatible software from transaction malleability. This flag, which compatible signing software enables by default, required that the transaction use a _fully-canonical_ signature to be valid. Now that the [RequireFullyCanonicalSig amendment][] is enabled, the flag is no longer necessary, but there is no harm in enabling it anyway.
### Malleability with Multi-Signatures
An important, explicit feature of multi-signing is that multiple different possible configurations can make a transaction valid. For example, an account can be configured so that signatures from any three of five signers could authorize a transaction. However, this inherently means that there can be several different variations of a valid transaction, each with a different identifying hash.
All of the following cases can lead to transaction malleability:
- If a transaction still has enough signatures to meet its quorum after removing one or more. Any third party could remove a signature and re-submit the transaction without it.
- If one can add a valid signature to a transaction that already has a quorum. Only an authorized signer of the sending account could create such a signature.
- If one can replace one signature from a transaction with another valid signature while maintaining a quorum. Only an authorized signer of the sending account could create such a signature.
Even if your authorized signers are not intentionally malicious, confusion or poor coordination could cause several signers to submit different valid versions of the same transaction.
#### Mitigations for Multi-Signature Malleability
**Good operational security can protect against these problems.** Generally, you can avoid transaction malleability problems when multi-signing if you follow good operational security practices, including the following:
- Do not collect more signatures than necessary.
- Either appoint one party to assemble a transaction after collecting the necessary number of signatures, or instruct signers pass the transaction instructions forward to be signed in a set order.
- Do not add unnecessary or untrusted signers to your multi-signing lists, even if the `weight` values associated with their keys are insufficient to authorize a transaction.
- Be prepared for the possibility that a transaction executes with a different hash and set of signatures than you expected. Carefully monitor your account's sent transactions (for example, using the [account_tx method][]).
- Monitor the `Sequence` number of your account (for example, using the [account_info method][]). This number always increases by exactly one when your account sends a transaction successfully, and never any other way. If the number does not match what you expect, you should check your recent transactions to confirm why. (Aside from malleable transactions, there are other ways this could happen, too. Perhaps you configured another application to send transactions for you. Maybe a malicious user gained access to your secret key. Or perhaps your application lost data and forgot about a transaction you sent already.)
- If you re-create transactions to be multi-signed, _do not_ change the `Sequence` number unless you have manually confirmed that the intended actions have not already executed.
- Confirm that the `tfFullyCanonicalSig` flag is enabled before signing.
For greater security, these guidelines provide multiple layers of protection.
## Exploit With Malleable Transactions
If the software you use to interface with the XRP Ledger sends malleable transactions, a malicious actor may be able to trick your software into losing track of a transaction's final outcome and potentially (in the worst case) sending equivalent payments multiple times.
If you use single-signatures only, you are not vulnerable to this exploit. If you use multi-signatures, you may be vulnerable if you or your signers provide more signatures than necessary.
### Exploit Scenario Steps
The process to exploit a vulnerable system follows a series of steps like the following:
1. The vulnerable system constructs a multi-signed transaction and collects more than the necessary number of signatures.
If an authorized signer is malicious or irresponsible, the transaction could also be vulnerable if that signer's signature is not included but could be added.
2. The system notes the identifying hash of the vulnerable transaction, submits it to the XRP Ledger network, then begins monitoring for that hash to be included in a validated ledger version.
3. A malicious actor sees the transaction propagating through the network before it becomes confirmed.
4. The malicious actor removes an extra signature from the vulnerable transaction.
Unlike creating a signature for different transaction instructions, this does not require a large amount of computational work. It can be done in much less time than it takes to generate a signature in the first place.
Alternatively, an authorized signer whose signature is not already part of the transaction could add their signature to the vulnerable transaction's list of signatures. Depending on the sender's multi-signing settings, this can be instead of or in addition to removing other signatures from the transaction.
The modified list of signatures results in a different identifying hash. (You do not have to calculate the hash before you submit to the network, but knowing the hash makes it easier to check the transaction's status later.)
5. The malicious actor submits the modified transaction to the network.
This creates a "race" between the transaction as originally submitted and the modified version submitted by the malicious actor. The two transactions are mutually exclusive. Both are valid, but they have the same exact transaction data, including the `Sequence` number, so at most one of them can ever be included in a validated ledger.
Servers in the peer-to-peer network have no way of knowing which one "came first" or was intended by its original sender. Delays or other coincidences in network connectivity could result in validators seeing only one or the other by the time they finalize their consensus proposals, so either one could "win the race".
A malicious actor could increase the chances of getting non-canonical transactions confirmed if they controlled some number of well-connected servers in the peer-to-peer network, even if those servers are not trusted as validators.
If the malicious actor controls the only server to which the vulnerable system submitted the transaction, the malicious actor can easily control which version is distributed to the rest of the network.
6. The malicious actor's version of the transaction achieves consensus and becomes included in a validated ledger.
At this point, the transaction has executed and cannot be reversed. Its effects (such as sending XRP) are final. The original version of the transaction is no longer valid because its `Sequence` number has been used.
The effects of the transaction in the XRP Ledger are exactly the same as if the original version had executed.
7. The vulnerable system does not see the transaction hash it is expecting, and erroneously concludes that the transaction did not execute.
If the transaction included the `LastLedgerSequence` field, this would occur after the specified ledger index has passed.
If the transaction omitted the `LastLedgerSequence` field, this could be wrong in another way: if no other transaction from the same sender uses the same `Sequence` number, then the transaction could theoretically succeed later regardless of how much time has passed. (See [Reliable Transaction Submission](../reliable-transaction-submission.md) for details.)
8. The vulnerable system takes action assuming that the transaction has failed.
For example, it may refund (or not debit) a customer's balance in its own system, to account for the funds that it thinks have not been sent in the XRP Ledger.
Worse, the vulnerable system might construct a new transaction to replace the transaction, picking new `Sequence`, `LastLedgerSequence`, and `Fee` parameters based on the current state of the network, but keeping the rest of the transaction the same as the original. If this new transaction is also malleable, the system could be exploited in the same way an indefinite number of times.
## See Also
- **Concepts:**
- [Transactions](../index.md)
- [Finality of Results](index.md)
- **Tutorials:**
- [Look Up Transaction Results](look-up-transaction-results.md)
- [Reliable Transaction Submission](../reliable-transaction-submission.md)
- **References:**
- [Basic Data Types - Hashes](../../../references/protocol/data-types/basic-data-types.md#hashes)
- [Transaction Common Fields - Global Flags](../../../references/protocol/transactions/common-fields.md#global-flags)
- [Transaction Results](../../../references/protocol/transactions/transaction-results/transaction-results.md)
- [Serialization Format](../../../references/protocol/binary-format.md)
{% raw-partial file="/_snippets/common-links.md" /%}

View File

@@ -0,0 +1,224 @@
---
html: transactions.html
parent: concepts.html
seo:
description: Transactions are the only way to change the XRP Ledger. Understand what forms they take and how to use them.
labels:
- Payments
- Transaction Sending
---
# Transactions
A _Transaction_ is the only way to modify the XRP Ledger. Transactions are only final if signed, submitted, and accepted into a validated ledger version following the [consensus process](../consensus-protocol/index.md). Some ledger rules also generate _[pseudo-transactions](../../references/protocol/transactions/pseudo-transaction-types/pseudo-transaction-types.md)_, which aren't signed or submitted, but still must be accepted by consensus. Transactions that fail are also included in ledgers because they modify balances of XRP to pay for the anti-spam [transaction cost][].
Transactions can do more than send money. In addition to supporting various [Payment Types](../payment-types/index.md), transactions in the XRP Ledger are also used to rotate [cryptographic keys](../accounts/cryptographic-keys.md), manage other settings, and trade in the XRP Ledger's [decentralized exchange](../tokens/decentralized-exchange/index.md). The [`rippled` API reference](../../references/http-websocket-apis/index.md) has a complete [list of transaction types](../../references/protocol/transactions/types/index.md).
### Identifying Transactions
Every signed transaction has a unique `"hash"` that identifies it. The server provides the hash in the response when you submit the transaction; you can also look up a transaction in an account's transaction history with the [account_tx command](../../references/http-websocket-apis/public-api-methods/account-methods/account_tx.md).
The transaction hash can be used as a "proof of payment" since anyone can [look up the transaction by its hash](finality-of-results/look-up-transaction-results.md) to verify its final status.
{% raw-partial file="/_snippets/setfee_uniqueness_note.md" /%}
## Claimed Cost Justification
Although it may seem unfair to charge a [transaction cost](transaction-cost.md) for a failed transaction, the `tec` class of errors exists for good reasons:
* Transactions submitted after the failed one do not have to have their Sequence values renumbered. Incorporating the failed transaction into a ledger uses up the transaction's sequence number, preserving the expected sequence.
* Distributing the transaction throughout the network increases network load. Enforcing a cost makes it harder for attackers to abuse the network with failed transactions.
* The transaction cost is generally very small in real-world value, so it should not harm users unless they are sending large quantities of transactions.
## Authorizing Transactions
In the decentralized XRP Ledger, a digital signature proves that a transaction is authorized to do a specific set of actions. Only signed transactions can be submitted to the network and included in a validated ledger. A signed transaction is immutable: its contents cannot change, and the signature is not valid for any other transaction. <!-- STYLE_OVERRIDE: is authorized to -->
A transaction can be authorized by any of the following types of signatures:
* A single signature from the master private key that is mathematically associated with the sending address. You can disable or enable the master key pair using an [AccountSet transaction][].
* A single signature that matches the regular private key associated with the address. You can add, remove, or replace a regular key pair using a [SetRegularKey transaction][].
* A [multi-signature](../accounts/multi-signing.md) that matches a list of signers owned by the address. You can add, remove, or replace a list of signers using a [SignerListSet transaction][].
Any signature type can authorize any type of transaction, with the following exceptions:
* Only the master private key can [disable the master public key](../../references/protocol/transactions/types/accountset.md).
* Only the master private key can [permanently give up the ability to freeze](../tokens/fungible-tokens/freezes.md#no-freeze).
* You can never remove the last method of signing transactions from an address.
For more information about master and regular key pairs, see [Cryptographic Keys](../accounts/cryptographic-keys.md).
<!--{# Add this reference after signatures concept doc is published. For more information about signatures, see [Understanding Signatures](concept-signatures.html). #}-->
## Signing and Submitting Transactions
Sending a transaction to the XRP Ledger involves several steps:
1. Create an [unsigned transaction in JSON format](#example-unsigned-transaction).
2. Use one or more signatures to [authorize the transaction](#authorizing-transactions).
3. Submit a transaction to an XRP Ledger server (usually a [`rippled` instance](../networks-and-servers/index.md)). If the transaction is properly formed, the server provisionally applies the transaction to its current version of the ledger and relays the transaction to other members of the peer-to-peer network.
4. The [consensus process](../consensus-protocol/index.md) determines which provisional transactions get included in the next validated ledger.
5. The servers apply those transactions to the previous ledger in a canonical order and share their results.
6. If enough [trusted validators](../networks-and-servers/rippled-server-modes.md#validators) created the exact same ledger, that ledger is declared _validated_ and the [results of the transactions](../../references/protocol/transactions/transaction-results/transaction-results.md) in that ledger are immutable.
See [Send XRP](../../tutorials/get-started/send-xrp.md) for an interactive tutorial in sending XRP payments.
### Example Unsigned Transaction
Here is an example of an unsigned [Payment transaction][] in JSON:
```json
{
"TransactionType" : "Payment",
"Account" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Destination" : "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Amount" : {
"currency" : "USD",
"value" : "1",
"issuer" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
},
"Fee": "12",
"Flags": 2147483648,
"Sequence": 2,
}
```
The XRP Ledger only relays and executes a transaction if the transaction object has been authorized by the sending address (in the `Account`) field. For instructions on how to do this securely, see [Set Up Secure Signing](secure-signing.md).
## Example Signed Transaction Blob
Signing a transaction results in a chunk of binary data, called a "blob", that can be submitted to the network. Here is an example of the same transaction, as a signed blob, being [submitted with the WebSocket API](../../references/http-websocket-apis/public-api-methods/transaction-methods/submit.md):
```json
{
"id": 2,
"command": "submit",
"tx_blob" : "120000240000000461D4838D7EA4C6800000000000000000000000000055534400000000004B4E9C06F24296074F7BC48F92A97916C6DC5EA968400000000000000F732103AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB74483046022100982064CDD3F052D22788DB30B52EEA8956A32A51375E72274E417328EBA31E480221008F522C9DB4B0F31E695AA013843958A10DE8F6BA7D6759BEE645F71A7EB240BE81144B4E9C06F24296074F7BC48F92A97916C6DC5EA983143E9D4A2B8AA0780F682D136F7A56D6724EF53754"
}
```
## Example Executed Transaction with Metadata
After a transaction has been executed, the XRP Ledger adds [metadata](../../references/protocol/transactions/metadata.md) to show the transaction's final outcome and all the changes that the transaction made to the shared state of the XRP Ledger.
You can check a transaction's status using the API, for example using the [tx command](../../references/http-websocket-apis/public-api-methods/transaction-methods/tx.md).
**Caution:** The results of a transaction, including all its metadata, are not final unless the transaction appears in a **validated** ledger. See also: [Finality of Results](finality-of-results/index.md).
Example response from the `tx` command:
```json
{
"id": 6,
"status": "success",
"type": "response",
"result": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Amount": {
"currency": "USD",
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value": "1"
},
"Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Fee": "10",
"Flags": 2147483648,
"Sequence": 2,
"SigningPubKey": "03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB",
"TransactionType": "Payment",
"TxnSignature": "3045022100D64A32A506B86E880480CCB846EFA3F9665C9B11FDCA35D7124F53C486CC1D0402206EC8663308D91C928D1FDA498C3A2F8DD105211B9D90F4ECFD75172BAE733340",
"date": 455224610,
"hash": "33EA42FC7A06F062A7B843AF4DC7C0AB00D6644DFDF4C5D354A87C035813D321",
"inLedger": 7013674,
"ledger_index": 7013674,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Balance": "99999980",
"Flags": 0,
"OwnerCount": 0,
"Sequence": 3
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
"PreviousFields": {
"Balance": "99999990",
"Sequence": 2
},
"PreviousTxnID": "7BF105CFE4EFE78ADB63FE4E03A851440551FE189FD4B51CAAD9279C9F534F0E",
"PreviousTxnLgrSeq": 6979192
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "2"
},
"Flags": 65536,
"HighLimit": {
"currency": "USD",
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value": "0"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"value": "100"
},
"LowNode": "0000000000000000"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "96D2F43BA7AE7193EC59E5E7DDB26A9D786AB1F7C580E030E7D2FF5233DA01E9",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "1"
}
},
"PreviousTxnID": "7BF105CFE4EFE78ADB63FE4E03A851440551FE189FD4B51CAAD9279C9F534F0E",
"PreviousTxnLgrSeq": 6979192
}
}
],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS"
},
"validated": true
}
}
```
## See Also
- **Concepts:**
- [Payment Types](../payment-types/index.md)
- **Tutorials:**
- [Set Up Secure Signing](secure-signing.md)
- [Send XRP](../../tutorials/get-started/send-xrp.md)
- [Look Up Transaction Results](finality-of-results/look-up-transaction-results.md)
- [Monitor Incoming Payments with WebSocket](../../tutorials/get-started/monitor-incoming-payments-with-websocket.md)
- [Cancel or Skip a Transaction](finality-of-results/canceling-a-transaction.md)
- [Reliable Transaction Submission](reliable-transaction-submission.md)
- **References:**
- [Transaction Common Fields](../../references/protocol/transactions/common-fields.md)
- [Transaction Types](../../references/protocol/transactions/types/index.md)
- [Transaction Metadata](../../references/protocol/transactions/metadata.md)
- [account_tx method][]
- [tx method][]
- [submit method][]
- [submit_multisigned method][]
{% raw-partial file="/_snippets/common-links.md" /%}

View File

@@ -0,0 +1,551 @@
---
html: reliable-transaction-submission.html
parent: transactions.html
seo:
description: Build a system that can submit transactions to the XRP Ledger and get their final results safely and quickly.
labels:
- Transaction Sending
- Development
---
# Reliable Transaction Submission
Financial institutions and other services using the XRP Ledger should use the best practices described here to make sure that transactions are validated or rejected in a verifiable and prompt way. You should submit transactions to trusted `rippled` servers.
The best practices detailed in this document allow applications to submit transactions to the XRP Ledger while achieving:
1. [Idempotency](https://en.wikipedia.org/wiki/Idempotence) - Transactions should be processed once and only once, or not at all.
2. Verifiability - Applications can determine the final result of a transaction.
Applications which fail to implement best practices are at risk of the following errors:
1. Submitting transactions which are inadvertently never executed.
2. Mistaking provisional transaction results for their final, immutable results.
3. Failing to find authoritative results of transactions previously applied to the ledger.
These types of errors can potentially lead to serious problems. For example, an application that fails to find a prior successful payment transaction might erroneously submit another transaction, duplicating the original payment. This underscores the importance that applications base their actions on authoritative transaction results, using the techniques described in this document.
## Background
The XRP Ledger protocol provides a ledger shared across all servers in the network. Through a [process of consensus and validation](../consensus-protocol/index.md), the network agrees on the order in which transactions are applied to (or omitted from) the ledger.
Well-formed transactions submitted to trusted XRP Ledger servers 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](transaction-cost.md) increases after an application sends a transaction. If the transaction cost increases above what has been specified in the transaction, the transaction is not included in the next validated ledger. If at some later date the global transaction cost decreases, the transaction could be included in a later ledger. If the transaction does not specify an expiration, there is no limit to how much later this can occur.
If a power or network outage occurs, applications face more challenges finding the status of submitted transactions. Applications must take care both to properly submit a transaction and later to properly get authoritative results.
### Transaction Timeline
When you submit a transaction to the XRP Ledger, regardless of whether you used [HTTP API](../../references/http-websocket-apis/index.md), a [client library](../../references/client-libraries.md), or some other app, process of applying the transaction to the ledger is the same. That process goes like this:
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.
- Well-formed transactions may provisionally succeed, and then later succeed in a slightly different way. (For example, the exchange rate when [trading currencies](../tokens/decentralized-exchange/index.md) may vary.)
3. Through consensus and validation, the transaction is applied to the ledger. Even some failed transactions are applied, 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:** A successful status code returned from a [submit method][] indicates the server has received the candidate transaction. The transaction may or may not be applied to a validated ledger.
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, `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 version and the ones that preceded it form the ledger history.
Each validated ledger version has a ledger index, which is 1 greater than the ledger index of the previous ledger version. 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 ledger index 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
<!-- SPELLING_IGNORE: lastledgersequence -->
`LastLedgerSequence` is an optional [parameter of all transactions](../../references/protocol/transactions/common-fields.md). This instructs the XRP Ledger that a transaction must be validated on or before a specific ledger version. The XRP Ledger never includes a transaction in a ledger version whose ledger index is higher than the transaction's `LastLedgerSequence` parameter.
Use the `LastLedgerSequence` parameter to prevent undesirable cases where a transaction is not confirmed promptly but could be included in a future ledger. You should specify the `LastLedgerSequence` parameter on every transaction. Automated processes should use a value of 4 greater than the last validated ledger index to make sure that a transaction is validated or rejected in a predictable and prompt way.
Applications using the [HTTP / WebSocket APIs](../../references/http-websocket-apis/index.md) should explicitly specify a `LastLedgerSequence` when submitting transactions. Some [client libraries](../../references/client-libraries.md) can also [auto-fill](../../references/protocol/transactions/common-fields.md#auto-fillable-fields) a reasonable value for `LastLedgerSequence`; the details vary by library.
## Best Practices
The following diagram summarizes the recommended flow for submitting a transaction and determining its outcome:
[{% inline-svg file="/img/reliable-tx-submission.svg" /%}](/img/reliable-tx-submission.svg "Reliable transaction submission flowchart")
### Reliable Transactions Submission
Applications submitting transactions should use the following practices to submit reliably even in the event that a process dies or other failure occurs. Application transaction results must be verified so that applications can act on the final, validated results.
Submission and verification are two separate procedures which may be implemented using the logic described in this document.
1. Submission - The transaction is submitted to the network and a provisional result is returned.
2. Verification - The authoritative result is determined by examining validated ledgers.
### Submission
[Persist](https://en.wikipedia.org/wiki/Persistence_%28computer_science%29) details of the transaction before submission, in case of power failure or network failure before submission completes. On restart, the persisted values make it possible to verify the status of the transaction.
The submission process:
1. Construct and sign the transaction
- Include `LastLedgerSequence` parameter
2. Persist the transaction details, saving:
- Transaction hash
- `LastLedgerSequence`
- Sender address and sequence number
- Latest validated ledger index at the time of submission
- Application-specific data, as needed
3. Submit the transaction
### Verification
During normal operation, applications may check the status of submitted transactions by their hashes; or, depending on the API used, receive notifications when transactions have been validated (or failed). This normal operation may be interrupted, for example by network failures or power failures. In case of such interruption applications need to reliably verify the status of transactions which may or may not have been submitted to the network before the interruption.
On restart, or the determination of a new last validated ledger (pseudocode):
```
For each persisted transaction without validated result:
Query transaction by hash
If (result appears in any validated ledger)
# Outcome is final
Persist the final result
If (result code is tesSUCCESS)
Application may act based on successful transaction
Else
The transaction failed (1)
If appropriate for the application and failure type, submit with
new LastLedgerSequence and Fee
Else if (LastLedgerSequence > newest validated ledger)
# Outcome is not yet final
Wait for more ledgers to be validated
Else
If (server has continuous ledger history from the ledger when the
transaction was submitted up to and including the ledger
identified by LastLedgerSequence)
# Sanity check
If (sender account sequence > transaction sequence)
A different transaction with this sequence has a final outcome.
Manual intervention suggested (3)
Else
The transaction failed (2)
Else
# Outcome is final, but not known due to a ledger gap
Wait to acquire continuous ledger history
```
#### Failure Cases
The difference between the two transaction failure cases (labeled (1) and (2) in the pseudo-code) is whether the transaction was included in a validated ledger. In both cases, you should decide carefully how to process the failure.
- In failure case (1), the transaction was included in a ledger and destroyed the [XRP transaction cost](transaction-cost.md), but did nothing else. This could be caused by a lack of liquidity, improperly specified [paths](../tokens/fungible-tokens/paths.md), or other circumstances. For many such failures, immediately retrying with a similar transaction is likely to have the same result. You may get different results if you wait for circumstances to change.
- In failure case (2), the transaction was not included in a validated ledger, so it did nothing at all, not even destroy the transaction cost. This could be the result of the transaction cost being too low for the current load on the XRP Ledger, the `LastLedgerSequence` being too soon, or it could be due to other conditions such as an unstable network connection.
- In contrast to failure case (1), it is more likely that a new transaction is likely to succeed if you change only the `LastLedgerSequence` and possibly the `Fee` and submit again. Use the same `Sequence` number as the original transaction.
- It is also possible that the transaction could not succeed due to the state of the ledger, for example because the sending address disabled the key pair used to sign the transaction. If the transaction's provisional result was a [`tef`-class code](../../references/protocol/transactions/transaction-results/tef-codes.md), the transaction is less likely to succeed without further modification.
- Failure case (3) represents an unexpected state. When a transaction is not processed, you should check the `Sequence` number of the sending account in the most recent validated ledger. (You can use the [account_info method][] to do so.) If the account's `Sequence` value in the latest validated ledger is higher than the transaction's `Sequence` value, then a different transaction with the same `Sequence` value has been included in a validated ledger. If your system is not aware of the other transaction, you are in an unexpected state and should stop processing until you have determined why that has happened; otherwise, your system might send multiple transactions trying to do the same thing. The steps you should take depend on specifically what caused it. Some possibilities include:
- The previously-sent transaction was [malleable](finality-of-results/transaction-malleability.md) and it actually was included in a validated ledger, but with a different hash than you expected. This can happen if you specify a set of flags that do not include the `tfFullyCanonicalSig` flag or if the transaction is multi-signed by more signers than necessary. If this is the case, save the different hash and the final outcome of the transaction, then resume normal activities.
- You [canceled](finality-of-results/canceling-a-transaction.md) and replaced the transaction, and the replacement transaction was processed instead. If you are recovering from an outage, it's possible you may have lost record of the replacement transaction. If this is the case, the transaction you were originally looking up has failed permanently, and the final outcome of the replacement transaction is recorded in a validated ledger version. Save both final outcomes, check for any other missing or replaced transactions, then resume normal activities.
- If you have two or more transaction-sending systems in an active/passive failover configuration, it's possible that the passive system mistakenly believes the active system has failed, and has become active while the original active system is still also sending transactions. Check the connectivity between the systems and ensure that at most one of them is active. Check your account's transaction history (for example, with the [account_tx method][]) and record the final outcome of all transactions that were included in validated ledgers. Any different transactions with the same `Sequence` numbers have failed permanently; save those final outcomes as well. When you have finished reconciling the differences from all the systems and have resolved the issues that made the systems activate simultaneously, resume normal activities.
**Tip:** The [`AccountTxnID` field](../../references/protocol/transactions/common-fields.md#accounttxnid) can help prevent redundant transactions from succeeding in this situation.
- A malicious actor may have used your secret key to send a transaction. If this is the case, [rotate your key pair](../../tutorials/manage-account-settings/change-or-remove-a-regular-key-pair.md) if you can, and check for other transactions sent. You should also audit your network to determine if the secret key was part of a larger intrusion or security leak. When you successfully rotate your key pair and are certain that the malicious actor no longer has access to your accounts and systems, you can resume normal activities.
#### Ledger Gaps
If your server does not have continuous ledger history from when the transaction was originally submitted up to and including the ledger identified by `LastLedgerSequence`, you may not know the final outcome of the transaction. (If it was included in one of the ledger versions your server is missing, you do not know whether it succeeded or failed.)
Your `rippled` server should automatically acquire the missing ledger versions when it has spare resources (CPU/RAM/disk IO) to do so, unless the ledgers are older than its [configured amount of history to store](../networks-and-servers/ledger-history.md). Depending on the size of the gap and the resource usage of your server, acquiring missing ledgers should take a few minutes. You can request your server to acquire historical ledger versions using the [ledger_request method][], but even so you may not be able to look up transaction outcomes from ledger versions that are outside of your server's configured history range.
Alternatively, you can look up the status of the transaction using a different `rippled` server that already has the needed ledger history, such as Ripple's full-history servers at `s2.ripple.com`. Only use a server you trust for this purpose. A malicious server could be programmed to provide false information about the status and outcome of a transaction.
## Technical Application
To implement the transaction submission and verification best practices, applications need to do the following:
1. Determine the signing account's next sequence number
* Each transaction has an account-specific [sequence number](../../references/protocol/data-types/basic-data-types.md#account-sequence). 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 index.
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.
How the application does these actions depends on the API the application uses. An application may use any of the following interfaces:
- The [HTTP / WebSocket APIs](../../references/http-websocket-apis/index.md) provided directly by XRP Ledger servers
- A [client library](../../references/client-libraries.md)
- Other middleware or APIs layered on top of the above APIs
### rippled - Submitting and Verifying Transactions
#### Determine the Account Sequence
`rippled` provides the [account_info method][] to learn an account's sequence number in the last validated ledger.
JSON-RPC Request:
```json
{
"method": "account_info",
"params": [
{
"account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"ledger_index": "validated"
}
]
}
```
Response body:
```json
{
"result": {
"validated": true,
"status": "success",
"ledger_index": 10266396,
"account_data": {
"index": "96AB97A1BBC37F4F8A22CE28109E0D39D709689BDF412FE8EDAFB57A55E37F38",
"Sequence": 4,
"PreviousTxnLgrSeq": 9905632,
"PreviousTxnID": "CAEE0E34B3DB50A7A0CA486E3A236513531DE9E52EAC47CE4C26332CC847DE26",
"OwnerCount": 2,
"LedgerEntryType": "AccountRoot",
"Flags": 0,
"Balance": "49975988",
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
}
}
}
```
In this example, the account's sequence is **4** (note `"Sequence": 4`, in `"account_data"`) as of the last validated ledger (note `"ledger_index": "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. To submit multiple transactions without waiting for validation of each, an application should keep a running account sequence number.
#### Determine the Last Validated Ledger
The [server_state method][] returns the ledger index of the last validated ledger.
Request:
```json
{
"id": "client id 1",
"method": "server_state"
}
```
Response:
```json
{
"result": {
"status": "success",
"state": {
"validation_quorum": 3,
"validated_ledger": {
"seq": 10268596,
"reserve_inc": 5000000,
"reserve_base": 20000000,
"hash": "0E0901DA980251B8A4CCA17AB4CA6C3168FE83FA1D3F781AFC5B9B097FD209EF",
"close_time": 470798600,
"base_fee": 10
},
"server_state": "full",
"published_ledger": 10268596,
"pubkey_node": "n9LGg37Ya2SS9TdJ4XEuictrJmHaicdgTKiPJYi8QRSdvQd3xMnK",
"peers": 58,
"load_factor": 256000,
"load_base": 256,
"last_close": {
"proposers": 5,
"converge_time": 3004
},
"io_latency_ms": 2,
"fetch_pack": 10121,
"complete_ledgers": "10256331-10256382,10256412-10268596",
"build_version": "0.26.4-sp3-private"
}
}
}
```
In this example the last validated ledger index is 10268596 (found under `result.state.validated_ledger` in the response). Note also this example indicates a gap in ledger history. The server used here would not be able to provide information about the transactions applied during that gap (ledgers 10256383 through 10256411). If configured to do so, the server eventually retrieves that part of the ledger history.
#### Construct the Transaction
`rippled` provides the [sign method][] 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) to another XRP Ledger address.
Request:
```json
{
"method": "sign",
"params": [
{
"offline": true,
"secret": "s████████████████████████████",
"tx_json": {
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Fee": "10000",
"Amount": {
"currency": "FOO",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"value": "10"
},
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"TransactionType": "Payment"
}
}
]
}
```
Notice the application specifies the account sequence `"Sequence": 4`, learned from an earlier call to `account_info`, to avoid `tefPAST_SEQ` errors.
Notice also the `LastLedgerSequence` based on the last validated ledger our application learned from `server_state`. The recommendation for backend applications is to use *(last validated ledger index + 4)*. Alternately, use a value of *(current ledger + 3)*. If `LastLedgerSequence` is miscalculated and less than the last validated ledger, the transaction fails with `tefMAX_LEDGER` error.
Response:
```json
{
"result": {
"tx_json": {
"hash": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"TxnSignature": "304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C753",
"TransactionType": "Payment",
"SigningPubKey": "0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Flags": 2147483648,
"Fee": "10000",
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"Amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
},
"tx_blob": "12000022800000002400000004201B009CAFB861D4C38D7EA4C68000000000000000000000000000464F4F0000000000AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A68400000000000271073210267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC57446304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C7538114AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A831438BC6F9F5A6F6C4E474DB0D59892E90C2C7CED5C",
"status": "success"
}
}
```
Applications should persist the transaction's hash before submitting. The result of the `sign` method includes the hash under `tx_json`.
#### Submit the transaction
`rippled` provides the [submit method][], allowing us to submit the signed transaction. This uses the `tx_blob` parameter that was returned by the `sign` method.
Request:
```json
{
"method": "submit",
"params": [
{
"tx_blob": "12000022800000002400000004201B009CAFB861D4C38D7EA4C68000000000000000000000000000464F4F0000000000AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A68400000000000271073210267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC57446304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C7538114AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A831438BC6F9F5A6F6C4E474DB0D59892E90C2C7CED5C"
}
]
}
```
Response:
```json
{
"result": {
"tx_json": {
"hash": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"TxnSignature": "304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C753",
"TransactionType": "Payment",
"SigningPubKey": "0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Flags": 2147483648,
"Fee": "10000",
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"Amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
},
"tx_blob": "12000022800000002400000004201B009CAFB861D4C38D7EA4C68000000000000000000000000000464F4F0000000000AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A68400000000000271073210267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC57446304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C7538114AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A831438BC6F9F5A6F6C4E474DB0D59892E90C2C7CED5C",
"status": "success",
"engine_result_message": "The transaction was applied.",
"engine_result_code": 0,
"engine_result": "tesSUCCESS"
}
}
```
This a **preliminary** result. Final results are only available from validated ledgers. The lack of a `"validated": true` field indicates that this is **not an immutable result**.
#### Verify the Transaction
The transaction hash, generated when the transaction was signed, is passed to the [tx method][] to retrieve the result of a transaction.
Request:
```json
{
"method": "tx",
"params": [
{
"transaction": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"binary": false
}
]
}
```
Response:
```json
{
"result": {
"validated": true,
"status": "success",
"meta": {
"TransactionResult": "tesSUCCESS",
"TransactionIndex": 2,
"AffectedNodes": [...]
},
"ledger_index": 10268599[d],
"inLedger": 10268599,
"hash": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"date": 470798270,
"TxnSignature": "304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C753",
"TransactionType": "Payment",
"SigningPubKey": "0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Flags": 2147483648,
"Fee": "10000",
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"Amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
}
}
```
This example response shows `"validated": true`, indicating the transaction has been included in a validated ledger, so the result of the transaction is immutable. Further, the metadata includes `"TransactionResult": "tesSUCCESS"`, indicating the transaction was applied to the ledger.
If the response does not include `"validated": true`, the result is provisional and subject to change. To retrieve a final result, applications must invoke the `tx` method again, allowing enough time for the network to validate more ledger versions. It may be necessary to wait for the ledger specified in `LastLedgerSequence` to be validated, although if the transaction is included in an earlier validated ledger the result becomes immutable at that time.
#### Verify Missing Transaction
Applications must handle cases where a call to the [tx method][] returns a `txnNotFound` error.
```json
{
"result": {
"status": "error",
"request": {
"transaction": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE56",
"command": "tx",
"binary": false
},
"error_message": "Transaction not found.",
"error_code": 24,
"error": "txnNotFound"
}
}
```
The `txnNotFound` result code occurs in cases where the transaction is not included in any ledger. However, it could also occur when a `rippled` instance does not have a complete ledger history, or if the transaction has not yet propagated to the `rippled` instance. Applications should make further queries to determine how to react.
The [server_state method][] (used earlier to determine the last validated ledger) indicates how complete the ledger history is, under `result.state.complete_ledgers`.
```json
{
"result": {
"status": "success",
"state": {
"validation_quorum": 3,
"validated_ledger": {
"seq": 10269447,
"reserve_inc": 5000000,
"reserve_base": 20000000,
"hash": "D05C7ECC66DD6F4FEA3A6394F209EB5D6824A76C16438F562A1749CCCE7EAFC2",
"close_time": 470802340,
"base_fee": 10
},
"server_state": "full",
"pubkey_node": "n9LJ5eCNjeUXQpNXHCcLv9PQ8LMFYy4W8R1BdVNcpjc1oDwe6XZF",
"peers": 84,
"load_factor": 256000,
"load_base": 256,
"last_close": {
"proposers": 5,
"converge_time": 2002
},
"io_latency_ms": 1,
"complete_ledgers": "10256331-10256382,10256412-10269447",
"build_version": "0.26.4-sp3-private"
}
}
}
```
Our example transaction specified `LastLedgerSequence` 10268600, based on the last validated ledger at the time, plus four. 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 cannot be included in any future ledger. In this case, application logic may dictate building and submitting a replacement transaction with the same account sequence and updated `LastLedgerSequence`.
The server may report a last validated ledger index less than the specified `LastLedgerSequence`. If so, the `txnNotFound` indicates either (a) the submitted transaction has not been 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 can be processed at most once.
Finally the server may show 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) to determine that a `txnNotFound` result is immutable.
## See Also
- [Transaction Formats](../../references/protocol/transactions/index.md)
- [Transaction Cost](transaction-cost.md)
- [Transaction Malleability](finality-of-results/transaction-malleability.md)
- [Overview of XRP Ledger Consensus Process](../consensus-protocol/index.md)
- [Consensus Principles and Rules](../consensus-protocol/consensus-principles-and-rules.md)
{% raw-partial file="/_snippets/common-links.md" /%}

View File

@@ -0,0 +1,153 @@
---
html: secure-signing.html
parent: transactions.html
seo:
description: Set up an environment where you can submit transactions securely.
labels:
- Security
- Development
---
# Secure Signing
To submit [transactions](index.md) to the XRP Ledger, you need a way to digitally sign them without compromising the security of your [secret keys](../accounts/cryptographic-keys.md). (If others gain access to your secret keys, they have as much control over your accounts as you do, and can steal or destroy all your money.) This page summarizes how to set up such an environment so you can sign transactions securely.
**Tip:** If you are not submitting transactions to the network, you can safely use a trustworthy public server, such as the ones run by Ripple, to monitor for incoming transactions or read other network activity. All transactions, balances, and data in the XRP Ledger are public.
There are several configurations with varying levels of security that may be acceptable for your situation. Choose one of the following that best fits your needs:
- [Run `rippled` locally](#run-rippled-locally), or [in the same LAN](#run-rippled-on-the-same-lan).
- [Use a client library](#use-a-client-library-with-local-signing) that can do local signing.
- [Use a dedicated signing device](#use-a-dedicated-signing-device) that supports XRP Ledger signatures.
- [Use a secure VPN to connect to a remote `rippled` machine](#use-a-secure-vpn-with-a-remote-rippled-server) you trust.
<!-- Source for all diagrams in this article: https://drive.google.com/drive/u/0/folders/1MFkzxtMYpS8tzdm-TjWbLSVgU0zAG9Vh -->
## Insecure Configurations
[{% inline-svg file="/img/insecure-signing-options.svg" /%}](/img/insecure-signing-options.svg "Diagram of insecure configurations")
Any configuration in which outside sources may gain access to your secret key is dangerous, and is likely to result in a malicious user stealing all your XRP (and anything else your XRP Ledger address has). Examples of such configurations include ones where you use the [sign method][] of someone else's `rippled` server over the internet, or you send your secret key in plain text over the internet to your own server.
You should maintain the secrecy of your secret keys at all times, which includes things like not emailing them to yourself, not typing them visibly in public, and saving them encrypted—never in plain text—when you are not using them. The balance between security and convenience depends in part on the value of your addresses' holdings, so you may want to use multiple addresses with different security configurations for different purposes.
<!-- Note: I'd link "issuing and operational addresses" for an explanation of hot/cold wallet security, but it's particularly gateway/issued-currency centric, which is not appropriate for this context. -->
## Run rippled Locally
[{% inline-svg file="/img/secure-signing-local-rippled.svg" /%}](/img/secure-signing-local-rippled.svg "Diagram of using a local rippled server for signing")
In this configuration, you run `rippled` on the machine that generates the transactions. Since the secret key never leaves your machine, no one without access to your machine can get access to the secret key. You should, of course, follow industry-standard practices for securing your machine. To use this configuration:
1. [Install `rippled`](../../infrastructure/installation/index.md).
Be sure that your local machine meets the minimum [system requirements for `rippled`](../../infrastructure/installation/system-requirements.md).
2. When you need to sign transactions, connect to your server on `localhost` or `127.0.0.1`. Use the [sign method][] (for single signatures) or [sign_for method][] (for multi-signatures).
The [example config file](https://github.com/XRPLF/rippled/blob/8429dd67e60ba360da591bfa905b58a35638fda1/cfg/rippled-example.cfg#L1050-L1073) listens for connections on the local loopback network (127.0.0.1), with JSON-RPC (HTTP) on port 5005 and WebSocket (WS) on port 6006, and treats all connected clients as admin.
**Caution:** Using the [commandline API](../../references/http-websocket-apis/api-conventions/request-formatting.md#commandline-format) for signatures is less secure than [using the Websocket or JSON-RPC APIs](../../tutorials/get-started/get-started-using-http-websocket-apis.md) through non-commandline clients. When using the commandline syntax, your secret key may be visible to other users in the system's process listing, and your shell history may save the key in plain text.
3. Maintain the server to keep it running, updated, and in sync with the network while you're using it.
**Note:** You _can_ turn off your `rippled` server when you're not sending transactions, but it can take up to 15 minutes to sync with the network when you start it up again.
## Run rippled on the same LAN
[{% inline-svg file="/img/secure-signing-lan-rippled.svg" /%}](/img/secure-signing-lan-rippled.svg "Diagram of using a rippled server over LAN for signing")
In this configuration, you run a `rippled` server on a dedicated machine in the same private local area network (LAN) as the machine that generates the transactions to be signed. This configuration lets you assemble transaction instructions on one or more machines with very modest system specs, while using a single dedicated machine for running `rippled`. This may appeal to you if you run your own datacenter or server room.
To use this configuration, set the `rippled` server to accept `wss` and `https` connections within your LAN. You can use a self-signed certificate if you use [certificate pinning](https://en.wikipedia.org/wiki/Transport_Layer_Security#Certificate_pinning), or you can use a certificate signed by an in-house or well-known Certificate Authority. Some certificate authorities, such as [Let's Encrypt](https://letsencrypt.org/) issue certificates automatically for free.
<!--{# TODO: link api-over-lan.html with the detailed instructions when those are ready #}-->
As always, follow industry-standard practices for securing your machines, such as using a firewall, anti-virus, appropriate user permissions, and so on.
## Use a Client Library with Local Signing
[{% inline-svg file="/img/secure-signing-client-library.svg" /%}](/img/secure-signing-client-library.svg "Diagram of using a client library with local signing")
This configuration uses a client library with built-in signing, in the programming language you use. For a list of libraries that can perform local signing, see [Client Libraries](../../references/client-libraries.md).
### Security Best Practices for Signing Libraries
To optimize the security of your signing library:
* Make sure the signing library you use has properly and securely implemented its signing algorithm(s). For example, if the library uses the default ECDSA algorithm, it should also use deterministic nonces as described in [RFC-6979](https://tools.ietf.org/html/rfc6979).
All of the published libraries listed above follow industry best practices.
* Keep your client library updated to the latest stable version.
* For enhanced security, you can load your secret keys from a management tool such as [Vault](https://www.vaultproject.io/).
### Local Signing Example
Here are examples of how to sign transaction instructions locally using the following languages and libraries:
* **JavaScript** / **TypeScript** - [`xrpl.js`](https://github.com/XRPLF/xrpl.js)
* **Python** - [`xrpl-py`](https://github.com/XRPLF/xrpl-py)
* **Java** - [`xrpl4j`](https://github.com/XRPLF/xrpl4j)
{% tabs %}
{% tab label="JavaScript" %}
{% code-snippet file="/_code-samples/secure-signing/js/signPayment.js" language="js" /%}
{% /tab %}
{% tab label="Python" %}
{% code-snippet file="/_code-samples/secure-signing/py/sign-payment.py" language="py" /%}
{% /tab %}
{% tab label="Java" %}
{% code-snippet file="/_code-samples/secure-signing/java/SignPayment.java" language="java" /%}
{% /tab %}
{% /tabs %}
## Use a Dedicated Signing Device
[{% inline-svg file="/img/secure-signing-dedicated-hardware.svg" /%}](/img/secure-signing-dedicated-hardware.svg "Diagram of using dedicated signing hardware")
Some companies sell dedicated signing devices, such as the [Ledger Nano S](https://www.ledger.com/products/ledger-nano-s), which are capable of signing XRP Ledger transactions using a secret key that never leaves the device. Some devices may not support all types of transactions.
Setting up this configuration depends on the specific device. You may need to run a "manager" application on your machine to interact with the signing device. See the manufacturer's instructions for how to set up and use such a device.
## Use a Secure VPN with a Remote rippled Server
[{% inline-svg file="/img/secure-signing-over-vpn.svg" /%}](/img/secure-signing-over-vpn.svg "Diagram of connecting securely to a remote rippled over VPN")
This configuration uses a `rippled` server hosted remotely, such as in a colocation facility or a distant datacenter, but connects to it securely using an encrypted VPN.
To use this configuration, follow the steps for [running `rippled` on a private LAN](#run-rippled-on-the-same-lan), but use a VPN to connect to the LAN of the remote `rippled` server. Instructions for setting up the VPN are specific to your environment and are not described in this guide.
## See Also
- **Concepts:**
- [Cryptographic Keys](../accounts/cryptographic-keys.md)
- [Multi-Signing](../accounts/multi-signing.md)
- **Tutorials:**
- [Install rippled](../../infrastructure/installation/index.md)
- [Assign a Regular Key Pair](../../tutorials/manage-account-settings/assign-a-regular-key-pair.md)
- [Reliable Transaction Submission](reliable-transaction-submission.md)
- [Enable Public Signing](../../infrastructure/configuration/enable-public-signing.md)
- **References:**
- [sign method][]
- [submit method][]
- [xrpl.js Reference](https://js.xrpl.org/)
- [`xrpl-py` Reference](https://xrpl-py.readthedocs.io/en/latest/index.html)
- [`xrpl4j` Reference](https://javadoc.io/doc/org.xrpl/)
{% raw-partial file="/_snippets/common-links.md" /%}

View File

@@ -0,0 +1,53 @@
---
html: source-and-destination-tags.html
parent: transactions.html
seo:
description: Use source and destination tags to indicate specific purposes for payments from and to multi-purpose addresses.
labels:
- Payments
- Accounts
- Security
---
# Source and Destination Tags
_Source tags_ and _destination tags_ are a feature of XRP Ledger [payments](../payment-types/index.md) that can indicate specific purposes for payments from and to multi-purpose addresses. Source and destination tags do not have direct on-ledger functionality; source and destination tags merely provide information about how off-ledger systems should process a payment. In transactions, both source and destination tags are formatted as 32-bit unsigned integers.
Destination tags indicate the beneficiary or destination for a payment. For example, a payment to an [exchange](../../use-cases/defi/list-xrp-as-an-exchange.md) or [stablecoin issuer](../../use-cases/tokenization/stablecoin-issuer.md) address can use a destination tag to indicate which customer to credit for the amount of the payment in that business's own systems. A payment to a merchant could indicate what item or cart the payment is buying.
Source tags indicate the originator or source of a payment. Most commonly, a Source Tag is included so that the recipient of the payment knows where to send a return, or "bounced", payment. When returning an incoming payment, you should use the source tag from the incoming payment as the destination tag of the outgoing (return) payment.
The practice of giving customers the ability to send and receive transactions from your XRP Ledger address using another interface is called providing _hosted accounts_. Hosted accounts typically use source and destination tags for each customer.
**Tip:** An [X-address](https://xrpaddress.info/) combines a classic address with a tag into a single address that encodes both. If you are showing a deposit address to customers, it may be easier for your customers to use an X-address rather than making them keep track of two pieces of information. (The tag in an X-address acts as a source tag when sending and a destination tag when receiving.)
## Rationale
In other distributed ledgers, it is common to use different deposit addresses for each customer. In the XRP Ledger, an address must be a funded, permanent [account](../accounts/accounts.md) to receive payments. Using this approach in the XRP Ledger wastefully consumes resources of all servers in the network, and is costly because the [reserve](../accounts/reserves.md) amount must be set aside indefinitely for each address.
Source and destination tags provide a more lightweight way to map deposits and payments to individual customers.
## Use Cases
A business may want to use source and destination tags for several purposes:
- Direct mappings to customer accounts.
- Matching return payments to the outgoing payments that prompted them.
- Mapping payments to quotes that expire.
- Providing disposable tags that customers can generate for specific deposits.
To prevent overlap while protecting privacy, a business can divide the total range of tags available into sections for each purpose, then assign tags in an unpredictable order within the range. For example, use a cryptographic hash function like [SHA-256](https://en.wikipedia.org/wiki/SHA-2), then use the [modulo operator](https://en.wikipedia.org/wiki/Modulo_operation) to map the output to the size of the relevant section. To be safe, check for collisions with old tags before using a new tag.
Assigning tags in numerical order provides less privacy to customers. Since all XRP Ledger transactions are public, assigning tags in this way can make it possible to guess which tags correspond to various users' addresses or to derive information about users' accounts based on the tags used.
## Requiring Tags
For an XRP Ledger address that may receive payments intended for several customer accounts, receiving a payment _without_ a destination tag can be a problem: it is not immediately obvious which customer to credit, which can require a manual intervention and a discussion with the sender to figure out who was the intended recipient. To reduce cases like this, you can [enable the `RequireDest` setting](../../tutorials/manage-account-settings/require-destination-tags.md). That way, if a user forgets to include a destination tag in a payment, the XRP Ledger rejects their payment instead of giving you money you don't know what to do with. The user can then send the payment again, using the tag as they should have.
## See Also
- [Require Destination Tags](../../tutorials/manage-account-settings/require-destination-tags.md)
- [Payment Types](../payment-types/index.md)
{% raw-partial file="/_snippets/common-links.md" /%}

View File

@@ -0,0 +1,177 @@
---
html: transaction-cost.html
parent: transactions.html
seo:
description: The transaction cost is a small amount of XRP destroyed to send a transaction, which protects the ledger from spam. Learn how the transaction cost applies.
labels:
- Fees
- Transaction Sending
---
# Transaction Cost
To protect the XRP Ledger from being disrupted by spam and denial-of-service attacks, each transaction must destroy a small amount of [XRP](../../introduction/what-is-xrp.md). This _transaction cost_ is designed to increase along with the load on the network, making it very expensive to deliberately or inadvertently overload the network.
Every transaction must [specify how much XRP to destroy](#specifying-the-transaction-cost) to pay the transaction cost.
## Current Transaction Cost
The current minimum transaction cost required by the network for a standard transaction is **0.00001 XRP** (10 drops). It sometimes increases due to higher than usual load.
You can also [query `rippled` for the current transaction cost](#querying-the-transaction-cost).
### Special Transaction Costs
Some transactions have different transaction costs:
| Transaction | Cost Before Load Scaling |
|-----------------------|--------------------------|
| [Reference Transaction](#reference-transaction-cost) (Most transactions) | 10 drops |
| [Key Reset Transaction](#key-reset-transaction) | 0 |
| [Multi-signed Transaction](../accounts/multi-signing.md) | 10 drops × (1 + Number of Signatures Provided) |
| [EscrowFinish Transaction with Fulfillment](../../references/protocol/transactions/types/escrowfinish.md) | 10 drops × (33 + (Fulfillment size in bytes ÷ 16)) |
| [AccountDelete Transaction](../accounts/deleting-accounts.md) | 2,000,000 drops |
## Beneficiaries of the Transaction Cost
The transaction cost is not paid to any party: the XRP is irrevocably destroyed.
## Load Cost and Open Ledger Cost
There are two thresholds for the transaction cost:
* If the transaction cost does not meet a `rippled` server's [load-based transaction cost threshold](#local-load-cost), the server ignores the transaction completely.
* If the transaction cost does not meet a `rippled` server's [open ledger cost threshold](#open-ledger-cost), the server queues the transaction for a later ledger.
This divides transactions into roughly three categories:
* Transactions that specify a transaction cost so low that they get rejected by the load-based transaction cost.
* Transactions that specify a transaction cost high enough to be included in the current open ledger.
* Transactions in between, which get [queued for a later ledger version](#queued-transactions).
## Local Load Cost
Each `rippled` server maintains a cost threshold based on its current load. If you submit a transaction with a `Fee` value that is lower than current load-based transaction cost of the `rippled` server, that server neither applies nor relays the transaction. (**Note:** If you submit a transaction through an [admin connection](../../tutorials/get-started/get-started-using-http-websocket-apis.md), the server applies and relays the transaction as long as the transaction meets the un-scaled minimum transaction cost.) A transaction is very unlikely to survive [the consensus process](../consensus-protocol/index.md) unless its `Fee` value meets the requirements of a majority of servers.
## Open Ledger Cost
The `rippled` server has a second mechanism for enforcing the transaction cost, called the _open ledger cost_. A transaction can only be included in the open ledger if it meets the open ledger cost requirement in XRP. Transactions that do not meet the open ledger cost may be [queued for a following ledger](#queued-transactions) instead.
For each new ledger version, the server picks a soft limit on the number of transactions to be included in the open ledger, based on the number of transactions in the previous ledger. The open ledger cost is equal to the minimum un-scaled transaction cost until the number of transactions in the open ledger is equal to the soft limit. After that, the open ledger cost increases exponentially for each transaction included in the open ledger. For the next ledger, the server increases the soft limit if the current ledger contained more transactions than the soft limit, and decreases the soft limit if the consensus process takes more than 5 seconds.
The open ledger cost requirement is [proportional to the normal cost of the transaction](#fee-levels), not the absolute transaction cost. Transaction types that have a higher-than-normal requirement, such as [multi-signed transactions](../accounts/multi-signing.md) must pay more to meet the open ledger cost than transactions which have minimum transaction cost requirements.
See also: [Fee Escalation explanation in `rippled` repository](https://github.com/XRPLF/rippled/blob/release/src/ripple/app/misc/FeeEscalation.md).
### Queued Transactions
When `rippled` receives a transaction that meets the server's local load cost but not the [open ledger cost](#open-ledger-cost), the server estimates whether the transaction is "likely to be included" in a later ledger. If so, the server adds the transaction to the transaction queue and relays the transaction to other members of the network. Otherwise, the server discards the transaction. The server tries to minimize the amount of network load caused by transactions that would not pay a transaction cost, since [the transaction cost only applies when a transaction is included in a validated ledger](#transaction-costs-and-failed-transactions).
For more information on queued transactions, see [Transaction Queue](transaction-queue.md).
## Reference Transaction Cost
The "Reference Transaction" is the cheapest (non-free) transaction, in terms of the necessary [transaction cost](transaction-cost.md) before load scaling. Most transactions have the same cost as the reference transaction. Some transactions, such as [multi-signed transactions](../accounts/multi-signing.md), require a multiple of this cost instead. When the open ledger cost escalates, the requirement is proportional to the basic cost of the transaction.
### Fee Levels
_Fee levels_ represent the proportional difference between the minimum cost and the actual cost of a transaction. The [Open Ledger Cost](#open-ledger-cost) is measured in fee levels instead of absolute cost. See the following table for a comparison:
| Transaction | Minimum cost in drops | Minimum cost in Fee levels | Double cost in drops | Double cost in fee levels |
|-------------|-----------------------|----------------------------|----------------------|---------------------------|
| Reference transaction (most transactions) | 10 | 256 | 20 | 512 |
| [Multi-signed transaction](../accounts/multi-signing.md) with 4 signatures | 50 | 256 | 100 | 512 |
| [Key reset transaction](transaction-cost.md#key-reset-transaction) | 0 | (Effectively infinite) | N/A | (Effectively infinite) |
| [EscrowFinish transaction](../../references/protocol/transactions/types/escrowfinish.md) with 32-byte preimage. | 350 | 256 | 700 | 512 |
## Querying the Transaction Cost
The `rippled` APIs have two ways to query the local load-based transaction cost: the `server_info` command (intended for humans) and the `server_state` command (intended for machines).
You can use the [fee method][] to check the open ledger cost.
### server_info
The [server_info method][] reports the unscaled minimum XRP cost, as of the previous ledger, as `validated_ledger.base_fee_xrp`, in the form of decimal XRP. The actual cost necessary to relay a transaction is scaled by multiplying that `base_fee_xrp` value by the `load_factor` parameter in the same response, which represents the server's current load level. In other words:
**Current Transaction Cost in XRP = `base_fee_xrp` × `load_factor`**
### server_state
The [server_state method][] returns a direct representation of `rippled`'s internal load calculations. In this case, the effective load rate is the ratio of the current `load_factor` to the `load_base`. The `validated_ledger.base_fee` parameter reports the minimum transaction cost in [drops of XRP](../../references/protocol/data-types/basic-data-types.md#specifying-currency-amounts). This design enables `rippled` to calculate the transaction cost using only integer math, while still allowing a reasonable amount of fine-tuning for server load. The actual calculation of the transaction cost is as follows:
**Current Transaction Cost in Drops = (`base_fee` × `load_factor`) ÷ `load_base`**
## Specifying the Transaction Cost
Every signed transaction must include the transaction cost in the [`Fee` field](../../references/protocol/transactions/common-fields.md). Like all fields of a signed transaction, this field cannot be changed without invalidating the signature.
As a rule, the XRP Ledger executes transactions _exactly_ as they are signed. (To do anything else would be difficult to coordinate across a decentralized consensus network, at the least.) As a consequence of this, every transaction destroys the exact amount of XRP specified by the `Fee` field, even if the specified amount is much more than the current minimum transaction cost for any part of the network. The transaction cost can even destroy XRP that would otherwise be set aside for an account's [reserve requirement](../accounts/reserves.md).
Before signing a transaction, we recommend [looking up the current load-based transaction cost](#querying-the-transaction-cost). If the transaction cost is high due to load scaling, you may want to wait for it to decrease. If you do not plan on submitting the transaction immediately, we recommend specifying a slightly higher transaction cost to account for future load-based fluctuations in the transaction cost.
### Automatically Specifying the Transaction Cost
The `Fee` field is one of the things that can be [auto-filled](../../references/protocol/transactions/common-fields.md#auto-fillable-fields) when creating a transaction. In this case, the auto-filling software provides a suitable `Fee` value based on the current load in the peer-to-peer network. However, there are several drawbacks and limitations to automatically filling in the transaction cost in this manner:
- If the network's transaction cost goes up between auto-filling and submitting the transaction, the transaction may not be confirmed.
- To prevent a transaction from getting stuck in a state of being neither definitively confirmed or rejected, be sure to provide a `LastLedgerSequence` parameter so it eventually expires. Alternatively, you can try to [cancel a stuck transaction](finality-of-results/canceling-a-transaction.md) by reusing the same `Sequence` number. See [reliable transaction submission](reliable-transaction-submission.md) for best practices.
- You have to be careful that the automatically provided value isn't too high. You don't want to burn a large fee to send a small transaction.
- If you are using `rippled`, you can also use the `fee_mult_max` and `fee_div_max` parameters of the [sign method][] to set a limit to the load scaling you are willing to sign.
- Some client libraries (like [xrpl.js](https://js.xrpl.org/) and [xrpl-py](https://xrpl-py.readthedocs.io/)) have configurable maximum `Fee` values, and raise an error instead of signing a transaction whose `Fee` value is higher than the maximum.
- You cannot auto-fill from an offline machine nor when [multi-signing](../accounts/multi-signing.md).
## Transaction Costs and Failed Transactions
Since the purpose of the transaction cost is to protect the XRP Ledger peer-to-peer network from excessive load, it should apply to any transaction that gets distributed to the network, regardless of whether or not that transaction succeeds. However, to affect the shared global ledger, a transaction must be included in a validated ledger. Thus, `rippled` servers try to include failed transactions in ledgers, with [`tec` status codes](../../references/protocol/transactions/transaction-results/transaction-results.md) ("tec" stands for "Transaction Engine - Claimed fee only").
The transaction cost is only debited from the sender's XRP balance when the transaction actually becomes included in a validated ledger. This is true whether the transaction is considered successful or fails with a `tec` code.
If a transaction's failure is [final](finality-of-results/index.md), the `rippled` server does not relay it to the network. The transaction does not get included in a validated ledger, so it cannot have any effect on anyone's XRP balance.
### Insufficient XRP
When a `rippled` server initially evaluates a transaction, it rejects the transaction with the error code `terINSUF_FEE_B` if the sending account does not have a high enough XRP balance to pay the XRP transaction cost. Since this is a `ter` (Retry) code, the `rippled` server retries the transaction without relaying it to the network, until the transaction's outcome is [final](finality-of-results/index.md).
When a transaction has already been distributed to the network, but the account does not have enough XRP to pay the transaction cost, the result code `tecINSUFF_FEE` occurs instead. In this case, the account pays all the XRP it can, ending with 0 XRP. This can occur because `rippled` decides whether to relay the transaction to the network based on its in-progress ledger, but transactions may be dropped or reordered when building the consensus ledger.
## Key Reset Transaction
As a special case, an account can send a [SetRegularKey](../../references/protocol/transactions/types/setregularkey.md) transaction with a transaction cost of `0`, as long as the account's [`lsfPasswordSpent` flag](../../references/protocol/ledger-data/ledger-entry-types/accountroot.md) is disabled. This transaction must be signed by the account's _master key pair_. Sending this transaction enables the `lsfPasswordSpent` flag.
This feature is designed to allow you to recover an account if the regular key is compromised, without worrying about whether the compromised account has any XRP available. This way, you can regain control of the account before you send more XRP to it.
The [`lsfPasswordSpent` flag](../../references/protocol/ledger-data/ledger-entry-types/accountroot.md) starts out disabled. It gets enabled when you send a SetRegularKey transaction signed by the master key pair. It gets disabled again when the account receives a [Payment](../../references/protocol/transactions/types/payment.md) of XRP.
`rippled` prioritizes key reset transactions above other transactions even though the nominal transaction cost of a key reset transaction is zero.
## Changing the Transaction Cost
The XRP Ledger has a mechanism for changing the minimum transaction cost to account for long-term changes in the value of XRP. Any changes have to be approved by the consensus process. See [Fee Voting](../consensus-protocol/fee-voting.md) for more information.
## See Also
- **Concepts:**
- [Reserves](../accounts/reserves.md)
- [Fee Voting](../consensus-protocol/fee-voting.md)
- [Transaction Queue](transaction-queue.md)
- **Tutorials:**
- [Reliable Transaction Submission](reliable-transaction-submission.md)
- **References:**
- [fee method][]
- [server_info method][]
- [FeeSettings object](../../references/protocol/ledger-data/ledger-entry-types/feesettings.md)
- [SetFee pseudo-transaction][]
{% raw-partial file="/_snippets/common-links.md" /%}

View File

@@ -0,0 +1,75 @@
---
html: transaction-queue.html
parent: transactions.html
seo:
description: Transactions can be queued before being processed.
labels:
- Transaction Sending
---
# Transaction Queue
The `rippled` server uses a transaction queue to help enforce the [open ledger cost](transaction-cost.md#open-ledger-cost). The open ledger cost sets a target number of transactions in a given ledger, and escalates the required transaction cost very quickly when the open ledger surpasses this size. Rather than discarding transactions that cannot pay the escalated transaction cost, `rippled` tries to put them in a transaction queue, which it uses to build the next ledger.
## Transaction Queue and Consensus
The transaction queue plays an important role in selecting the transactions that are included or excluded from a given ledger version in the consensus process. The following steps describe how the transaction queue relates to the [consensus process](../consensus-protocol/index.md).
[{% inline-svg file="/img/consensus-with-queue.svg" /%}](/img/consensus-with-queue.svg "Transaction queue and consensus diagram")
1. **Consensus Round 1** - Each validator proposes a set of transactions to be included in the next ledger version. Each also keeps a queue of candidate transactions not currently proposed.
2. **Consensus Round 2** - If a validator removes a transaction from its proposal in later rounds, it adds that transaction to its queue.
3. **Consensus Round N** - The consensus process continues until enough servers agree on a transaction set.
4. **Validation** - Servers confirm that they built the same resulting ledger and declare it validated.
5. **Building the Next Proposal** - Each validator prepares its proposal for the next ledger version, starting with queued transactions.
6. **Adding to the Queue** - If the next proposed ledger is already full, incoming transactions are queued for a later ledger version. (Transactions that pay the [open ledger cost](transaction-cost.md#open-ledger-cost) can still get into the next proposed ledger even if it's "full", but the open ledger cost grows exponentially with each transaction added this way.)
After this step, the process repeats from the beginning.
**Note:** Technically, several of the steps described in the above process occur in parallel, because each server is always listening for new transactions, and starts preparing its next ledger proposal while the consensus process for the previous ledger version is ongoing.
## Queuing Restrictions
The `rippled` server uses a variety of heuristics to estimate which transactions are "likely to be included in a ledger." The current implementation uses the following rules to decide which transactions to queue:
- Transactions must be properly-formed and [authorized](index.md#authorizing-transactions) with valid signatures.
- Transactions with an `AccountTxnID` field cannot be queued.
- A single sending address can have at most 10 transactions queued at the same time.
- To queue a transaction, the sender must have enough XRP for all of the following:
- Destroying the XRP [transaction cost](transaction-cost.md) as specified in the `Fee` fields of all the sender's queued transactions. The total amount among queued transactions cannot be more than the base account reserve (currently 10 XRP). (Transactions paying significantly more than the minimum transaction cost of 0.00001 XRP typically skip the queue and go straight into the open ledger.)
- Sending the maximum sum of XRP that all the sender's queued transactions could send.
- Keeping enough XRP to meet the account's [reserve requirements](../accounts/reserves.md).
- If a transaction affects how the sending address authorizes transactions, no other transactions from the same address can be queued behind it.
- If a transaction includes a `LastLedgerSequence` field, the value of that field must be at least **the current ledger index + 2**.
### Fee Averaging
If a sending address has one or more transactions queued, that sender can "push" the existing queued transactions into the open ledger by submitting a new transaction with a high enough transaction cost to pay for all of them. Specifically, the new transaction must pay a high enough transaction cost to cover the [open ledger cost](transaction-cost.md#open-ledger-cost) of itself and each other transaction from the same sender before it in the queue. (Keep in mind that the open ledger cost increases exponentially each time a transaction pays it.) The transactions must still follow the other queuing restrictions and the sending address must have enough XRP to pay the transaction costs of all the queued transactions.
This feature helps you work around a particular situation. If you submitted one or more transactions with a low cost that were queued, you cannot send new transactions from the same address unless you do one of the following:
* Wait for the queued transactions to be included in a validated ledger, _or_
* Wait for the queued transactions to be permanently invalidated if the transactions have the [`LastLedgerSequence` field](reliable-transaction-submission.md#lastledgersequence) set, _or_
* [Cancel the queued transactions](finality-of-results/canceling-a-transaction.md) by submitting a new transaction with the same sequence number and a higher transaction cost.
If none of the above occur, transactions can stay in the queue for a theoretically unlimited amount of time, while other senders can "cut in line" by submitting transactions with higher transaction costs. Since signed transactions are immutable, you cannot increase the transaction cost of the queued transactions to increase their priority. If you do not want to invalidate the previously submitted transactions, fee averaging provides a workaround. If you increase the transaction cost of your new transaction to compensate, you can ensure the queued transactions are included in an open ledger right away.
## Order Within the Queue
Within the transaction queue, transactions are ranked so that transactions paying a higher transaction cost come first. This ranking is not by the transactions' _absolute_ XRP cost, but by costs _relative to the [minimum cost for that type of transaction](transaction-cost.md#special-transaction-costs)_. Transactions that pay the same transaction cost are ranked in the order the server received them. Other factors may also affect the order of transactions in the queue; for example, transactions from the same sender are sorted by their `Sequence` numbers so that they are submitted in order.
The precise order of transactions in the queue decides which transactions get added to the next in-progress ledger version in cases where there are more transactions in the queue than the expected size of the next ledger version. The order of the transactions **does not affect the order the transactions are executed within a validated ledger**. In each validated ledger version, the transaction set for that version executes in [canonical order](../consensus-protocol/consensus-structure.md#calculate-and-share-validations).
**Note:** When `rippled` queues a transaction, the provisional [transaction response code](../../references/protocol/transactions/transaction-results/transaction-results.md) is `terQUEUED`. This means that the transaction is likely to succeed in a future ledger version. As with all provisional response codes, the outcome of the transaction is not final until the transaction is either included in a validated ledger, or [rendered permanently invalid](finality-of-results/index.md).
## See Also
- [Transaction Cost](transaction-cost.md) for information on why the transaction cost exists and how the XRP Ledger enforces it.
- [Consensus](../consensus-protocol/index.md) for a detailed description of how the consensus process approves transactions.
{% raw-partial file="/_snippets/common-links.md" /%}