Files
xrpl-dev-portal/content/tx_format.md
2014-11-06 18:01:54 -08:00

57 KiB
Raw Blame History

Transactions

A Transaction is the only way to modify the Ripple Ledger. All transactions have certain fields in common:

There are several different types of transactions that perform different actions, each with additional fields relevant to that type of action:

Additionally, there are Psuedo-Transactions that are not created and submitted in the usual way, but may appear in ledgers:

Signing and Sending Transactions

Signing a transaction cryptographically proves that the person in charge of the account sending the transaction is authorized to do so. 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.

You can sign a transaction using a secret key: either the master secret, or a regular secret if the account has a regular key pair associated with it. (See SetRegularKey for details.)

Multi-signature transactions are in development.

Typically, you create a transaction in JSON format first. Here is an example of an unsigned Payment-type transaction in JSON:

{
  "TransactionType" : "Payment",
  "Account" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
  "Destination" : "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
  "Amount" : {
     "currency" : "USD",
     "value" : "1",
     "issuer" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
  },
  "Fee": "10",
  "Flags": 2147483648,
  "Sequence": 2,
}

After doing that, you generate the signed binary format for the transaction. There are two ways to do this:

  1. Convert it to a binary blob and sign it offline. This is preferable, since it means that the account secret used for signing the transaction is never transmitted over any network connection.
  • rsign.js is a JavaScript implementation of offline signing.
  1. Have a rippled server sign the transaction for you. The sign command takes a JSON-format transaction and secret and returns the signed binary transaction format ready for submission. (Transmitting your account secret is dangerous, so you should only do this from within a trusted and encrypted sub-net, to a server you control.)
  • As a shortcut, you can use the submit command with a tx_json object to sign and submit a transaction all at once. This is only recommended for testing and development purposes.

In either case, signing a transaction generates a binary blob that can be submitted to the network. This means using rippled's submit command. Here is an example of the same transaction, as a signed blob, being submitted with the WebSocket API:

{
  "id": 2,
  "command": "submit",
  "tx_blob" : "120000240000000461D4838D7EA4C6800000000000000000000000000055534400000000004B4E9C06F24296074F7BC48F92A97916C6DC5EA968400000000000000F732103AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB74483046022100982064CDD3F052D22788DB30B52EEA8956A32A51375E72274E417328EBA31E480221008F522C9DB4B0F31E695AA013843958A10DE8F6BA7D6759BEE645F71A7EB240BE81144B4E9C06F24296074F7BC48F92A97916C6DC5EA983143E9D4A2B8AA0780F682D136F7A56D6724EF53754"
}

After a transaction has been submitted, if it gets accepted into a validated ledger, you can view the final transaction using the API. For example, here is what the WebSocket API tx command shows for the same transaction. The field names that begin with capital letters are part of the ledger object; the fields that begin with lower-case letters are additional information generated by the server for the request:

{
  "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
  }
}

Identifying Transactions

The "hash" is the unique value that identifies a particular transaction. 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.

The transaction hash can be used as a "proof of payment" since anyone can look up the transaction using the hash and verify its final status.

Common Fields

Every transaction type has the same set of fundamental fields:

Field JSON Type Internal Type Description
Account String Account The unique address of the account that initiated the transaction.
AccountTxnID String Hash256 (Optional) Hash value identifying another transaction. This transaction is only valid if the sending account's previously-sent transaction matches the provided hash.
Fee String Amount (Required, but auto-fillable) Integer amount of XRP, in drops, to be destroyed as a fee for redistributing this transaction to the network.
Flags Unsigned Integer UInt32 (Optional) Set of bit-flags for this transaction.
LastLedgerSequence Number UInt32 (Optional, but strongly recommended) Highest ledger sequence number that a transaction can appear in.
Memos Array of Objects Array (Optional) Additional arbitrary information used to identify this transaction.
Sequence Unsigned Integer UInt32 (Required, but auto-fillable) The sequence number, relative to the initiating account, of this transaction. A transaction is only valid if the Sequence number is exactly 1 greater than the last-valided transaction from the same account.
SigningPubKey String PubKey (Automatically added when signing) Hex representation of the public key that corresponds to the private key used to sign this transaction.
SourceTag Unsigned Integer UInt32 (Optional) Arbitrary integer used to identify the reason for this payment, or the hosted wallet on whose behalf this transaction is made. Conventionally, a refund should specify the initial payment's SourceTag as the refund payment's DestinationTag.
TransactionType String UInt16 The type of transaction. Valid types include: Payment, OfferCreate, OfferCancel, TrustSet, AccountSet, and SetRegularKey.
TxnSignature String VariableLength (Automatically added when signing) The signature that verifies this transaction as originating from the account it says it is from.

The field PreviousTxnID is DEPRECATED. It has been replaced by AccountTxnID. Always use AccountTxnID instead.

Auto-fillable Fields

Some fields can be automatically filled in before the transaction is signed, either by a rippled server or by the library used for offline signing. Both ripple-lib and rippled can automatically provide the following values:

  • Fee - Automatically use the current base network fee. (Note: rippled's sign command supports limits on how high the filled-in-value is, using the fee_mult_max parameter.)
  • Sequence - Automatically use the next sequence number for the account sending the transaction.

For a production system, we recommend not leaving these fields to be filled by the server. For example if fees become temporarily high, you may want to wait for fees to decrease before sending some transactions, instead of continuing regardless of fee.

The Paths field of the Payment transaction type can also be automatically filled in.

Transaction Fees

The Fee field specifies an amount, in drops of XRP, that must be deducted from the sender's balance in order to relay any transaction through the network. This is a measure to protect against spam and DDoS attacks weighing down the whole network. You can specify any amount in the Fee field when you create a transaction. If your transaction makes it into a validated leger (whether or not it achieves its intended purpose), then the deducted XRP is destroyed forever.

Each rippled server decides on the minimum fee to require, which is at least the global base transaction fee, and increases based on the individual server's current load. If a transaction's fee is not high enough, then the server does not relay the transaction to other servers. (Exception: If you send a transaction to your own server over an admin connection, it relays the transaction even under high load, so long as the fee meets the global base.)

Even if some servers have too much load to propagate a transaction, the transaction can still make it into a validated ledger as long as a large enough percentage of validating servers receive it, so the global base fee is generally enough to submit a transaction. If many servers in the network are under high load all at once (for example, due to a DDoS or a global event of some sort) then you must either set the fee higher or wait for the load to decrease.

For more information, see the Transaction Fee wiki article.

Canceling or Skipping a Transaction

An important and intentional feature of the Ripple Network is that a transaction is final as soon as it has been incorporated in a validated ledger.

However, if a transaction has not yet been included in a validated ledger, you can effectively cancel it by rendering it invalid. Typically, this means sending another transaction with the same Sequence value from the same account. If you do not want to perform the same transaction again, you can perform an AccountSet transaction with no options.

For example, if you attempted 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 fee 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 fee 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" transaction.

LastLedgerSequence

We strongly recommend that you specify the LastLedgerSequence parameter on every transaction. Provide a value of about 3 higher than the most recent ledger index to ensure that your transaction is either validated or rejected within a matter of seconds.

Without the LastLedgerSequence parameter, there is a particular situation that can occur and cause your transaction to be stuck in an undesirable state where it is neither validated nor rejected for a long time. Specifically, if the global base transaction fee increases after you send a transaction, your transaction may not get propagated enough to be included in a validated ledger, but you would have to pay the (increased) fee in order to send another transaction canceling it. Later, if the transaction fee decreases again, the transaction may become viable again. The LastLedgerSequence places a hard upper limit on how long the transaction can wait to be validated or rejected.

AccountTxnID

The AccountTxnID field lets you chain your transactions together, so that a current transaction is not valid unless the previous one (by Sequence Number) is also valid and matches the transaction you expected.

One situation in which this is useful is if you have a primary system for submitting transactions and a passive backup system. If the passive backup system becomes disconnected from the primary, but the primary is not fully dead, and they both begin operating at the same time, you could potentially encounter serious problems like some transactions sending twice and others not at all. Chaining your transactions together with AccountTxnID ensures that, even if both systems are active, only one of them can submit valid transactions at a time.

In order to use AccountTxnID, you must first set the asfAccountTxnID flag, so that the ledger keeps track of the ID for the account's previous transaction.

Memos

The Memos field allows for arbitrary messaging data that can accompany the transaction. It is presented as an array of objects. Each object has only one field, Memo, which in turn contains another object with one or more of the following fields:

Field Type Internal Type Description
MemoType String VariableLength Arbitrary hex value; conventionally should be ASCII for a unique relation (according to RFC 5988) that defines the format of this memo.
MemoData String VariableLength Arbitrary hex value, conventionally containing the content of the memo.
MemoFormat String VariableLength Arbitrary hex value, conventionally containing information on how the memo is encoded, for example as a MIME type

The Memos field is currently limited to no more than 1KB in size (when serialized in binary format).

Example of a transaction with a Memos field:

{
    "TransactionType": "Payment",
    "Account": "rMmTCjGFRWPz8S2zAUUoNVSQHxtRQD4eCx",
    "Destination": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
    "Memos": [
        {
            "Memo": {
                "MemoType": "5061796d656e7420726561736f6e",
                "MemoData": "72656e74"
            }
        }
    ],
    "Amount": "1"
}

Flags

The Flags field allows for additional boolean options regarding the behavior of a transaction. They are represented as binary values that can be combined with bitwise-or operations to set multiple flags at once.

Most flags only have meaning for a specific transaction type. The same bitwise value may be reused for flags on different transaction types, so it is important to pay attention to the TransactionType field when setting and reading flags.

The only flag that applies globally to all transactions is as follows:

Flag Name Hex Value Decimal Value Description
tfFullyCanonicalSig 0x80000000 2147483648 Require a fully-canonical signature, to protect a transaction from transaction malleability exploits.

Payment

[Source]

A Payment transaction represents a transfer of value from one account to another. (Depending on the path taken, additional exchanges of value may occur atomically to facilitate the payment.)

Payments are also the only way to create accounts.

Example payment:

{
  "TransactionType" : "Payment",
  "Account" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
  "Destination" : "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
  "Amount" : {
     "currency" : "USD",
     "value" : "1",
     "issuer" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
  },
  "Fee": "10",
  "Flags": 2147483648,
  "Sequence": 2,
}
Field JSON Type Internal Type Description
Amount String (XRP)
Object (Otherwise)
Amount The amount of currency to deliver. Note: If the tfPartialPayment flag is set, this is not the amount actually received. (Formatted as per Specifying Currency Amounts.)
Destination String Account The unique address of the account receiving the payment.
DestinationTag Unsigned Integer UInt32 (Optional) Arbitrary tag that identifies the reason for the payment to the destination, or the hosted wallet to make a payment to.
InvoiceID String Hash256 (Optional) Arbitrary 256-bit hash representing a specific reason or identifier for this payment.
Paths Array of path arrays PathSet (Optional, auto-fillable) Array of payment paths to be used for this transaction. Must be omitted for XRP-to-XRP transactions.
SendMax String/Object Amount (Optional) Highest amount of source currency this transaction is allowed to cost, including fees, exchanges, and slippage. (See Specifying Currency Amounts) Must be supplied for cross-currency/cross-issue payments (implies source balance). Must be omitted for XRP-to-XRP payments.

Creating Accounts

The Payment transaction type is the only way to create new accounts in the shared Ripple ledger. To do so, send an amount of XRP that is equal or greater than the account reserve to a mathematically-valid account address that does not exist yet. When the payment is processed, a new AccountRoot node will be added to the ledger to reflect the newly-created account.

If you attempt to send an insufficient amount of XRP, or any other currency, the Payment will fail.

Paths

The Paths field is a set of different paths along which the payment can be made. A single transaction can potentially follow multiple paths, for example if the transaction exchanges currency using several different offers in order to achieve the best rate. The source and destination (that is, the endpoints of the path) are omitted from a path. You can get suggestions of paths from rippled servers using the path_find or ripple_path_find commands.

If the Paths field is provided, the server decides at transaction processing time which paths to use out of the provided set. This decision is deterministic and attempts to minimize costs, but it is not guaranteed to be perfect.

If the Paths field is omitted, by definition the payment should take a direct path connecting the source and destination accounts. A direct path could be:

  • An XRP-to-XRP transfer.
  • A payment along a single trust line that connects the two accounts in the currency being transferred.

The Paths field must not be an empty array, nor an array whose members are all empty arrays.

The Paths field can be automatically filled in when the transaction is signed, if the build_path field is provided to the sign or submit commands. However, we recommend pathfinding separately and confirming the results prior to signing, in order to avoid surprises. There are no guarantees on how expensive the paths the server finds will be at the time of submission. (Although rippled is designed to search for the cheapest paths possible, it may not always find them. Untrustworthy rippled instances could also be modified to change this behavior for profit.)

Payment Flags

Transactions of the Payment type support additional values in the Flags field, as follows:

Flag Name Hex Value Decimal Value Description
tfNoDirectRipple 0x00010000 65536 Do not use a direct path, if available. This is intended to force the transaction to take arbitrage opportunities. Most clients will not need this.
tfPartialPayment 0x00020000 131072 If the specified Amount cannot be sent without spending more than SendMax, reduce the received amount instead of failing outright. See Partial Payments for more details.

Partial Payments

A partial payment allows a payment to succeed by reducing the amount received, instead of increasing the SendMax. Partial payments are useful for returning payments without incurring additional costs to oneself.

By default, the Amount field of a Payment transaction specifies the amount of currency that is received by the account that is the destination of the payment. Any additional amount needed for fees or currency exchange is deducted from the sending account's balances, up to the SendMax amount. (If SendMax is not specified, that is equivalent to setting the SendMax to the Amount field.) If the amount needed in order to make the payment exceeds the SendMax parameter, or the full amount cannot be delivered for any other reason, the transaction fails.

The tfPartialPayment flag allows you to make a "partial payment" instead. When this flag is enabled for a payment, it delivers as much as possible, up to the Amount value, without exceeding the SendMax value. Fees and currency exchange rates are calculated the same way, but the amount being sent automatically scales down until the total amount deducted from the sending account's balances is within SendMax. The transaction is always considered successful as long as it delivers any positive amount. This means that partial payments can succeed at sending some of the intended value despite limitations including fees, lack of liquidity, insufficient space in the receiving account's trustlines, or other reasons.

A partial payment cannot provide the initial XRP to fund an account; this case returns the error code telNO_DST_PARTIAL. Direct XRP-to-XRP payments also cannot be partial payments temBAD_SEND_XRP_PARTIAL.

The amount of XRP used for the transaction fee is always deducted from the senders account, regardless of the tfPartialPayment flag.

Partial Payments Warning

When the tfPartialPayment flag is enabled, the Amount field is not guaranteed to be the amount received. In fact, there is no minimum guaranteed amount that a partial payment actually delivers. Validated partial payment transactions have a meta.DeliveredAmount field, which indicates the amount of currency actually received by the destination account. When receiving a payment, you should confirm that, if the meta.DeliveredAmount field exists, you use that to determine how much your account received instead of relying on the Amount field.

Note: Early partial payments in historical ledgers do not have this field. If necessary, you can check the balance changes in the meta field to determine how much the destination account actually received.

AccountSet

[Source]

An AccountSet transaction modifies the properties of an [account in the global ledger]((https://wiki.ripple.com/Ledger_Format#AccountRoot).

Example AccountSet:

{
    "TransactionType": "AccountSet",
    "Account" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
    "Fee": "12",
    "Sequence": 5,
    "Domain": "6D64756F31332E636F6D",
    "LastLedgerIndex": 8804315,
    "SetFlag": 5,
    "MessageKey": "rQD4SqHJtDxn5DDL7xNnojNa3vxS1Jx5gv"
}
Field JSON Type Internal Type Description
ClearFlag Unsigned Integer UInt32 (Optional) Unique identifier of a flag to disable for this account.
Domain String VariableLength (Optional) The domain that owns this account, as a string of hex representing the ASCII for the domain in lowercase.
EmailHash String Hash128 (Optional) Hash of an email address to be used for generating an avatar image. Conventionally, clients use Gravatar to display this image.
MessageKey String PubKey (Optional) Public key for sending encrypted messages to this account. Conventionally, it should be a secp256k1 key, the same encryption that is used by the rest of Ripple.
SetFlag Unsigned Integer UInt32 (Optional) Integer flag to enable for this account.
TransferRate Unsigned Integer UInt32 (Optional) The fee to charge when users transfer this account's issuances, represented as billionths of a unit. Use 0 to set no fee.
WalletLocator String Hash256 (Optional) Not used.
WalletSize Unsigned Integer UInt32 (Optional) Not used.

If none of these options are provided, then the AccountSet transaction has no effect (beyond destroying the transaction fee). See Canceling or Skipping a Transaction for more details.

Domain

The Domain field is represented as the hex string of the lowercase ASCII of the domain. For example, the domain example.com would be represented as "6578616d706c652e636f6d".

To remove the Domain field from an account, send an AccountSet with the Domain set to an empty string.

Client applications can use the ripple.txt file hosted by the domain to confirm that the account is actually operated by that domain. Note: We expect the host-meta component of Gateway Services to replace ripple.txt for this purpose.

AccountSet Flags

There are several options which can be either enabled or disabled for an account. Account options are represented by different types of flags depending on the situation:

  • The AccountSet transaction type has several "AccountSet Flags" (prefixed asf) that can enable an option when passed as the SetFlag parameter, or disable an option when passed as the ClearFlag parameter.
  • The AccountSet transaction type has several transaction flags (prefixed tf) that can be used to enable or disable specific account options when passed in the Flags parameter. This style is discouraged, and new account options will not have new corresponding transaction flags.
  • The AccountRoot ledger node type has several ledger-specific-flags (prefixed lsf) which represent the state of particular account options within a particular ledger. Naturally, the values apply until a later ledger version changes them.

The preferred way to enable and disable Account Flags is using the SetFlag and ClearFlag parameters of an AccountSet transaction. AccountSet flags have names that begin with asf.

All flags are off by default.

The available AccountSet flags are:

Flag Name Decimal Value Description Corresponding Ledger Flag
asfRequireDest 1 Require a destination tag to send transactions to this account. lsfRequireDestTag
asfRequireAuth 2 Require authorization for users to hold balances issued by this account. (This prevents users unknown to a gateway from holding funds issued by that gateway.) lsfRequireAuth
asfDisallowXRP 3 XRP should not be sent to this account. (Enforced by client applications, not by rippled) lsfDisallowXRP
asfDisableMaster 4 Disallow use of the master key. Can only be enabled if the account has a RegularKey configured. lsfDisableMaster
asfAccountTxnID 5 Track the ID of this account's most recent transaction. Required for AccountTxnID (None)
asfNoFreeze 6 Permanently give up the ability to freeze individual trust lines. This flag can never be cleared. lsfNoFreeze
asfGlobalFreeze 7 Freeze all assets issued by this account. lsfGlobalFreeze

The following Transaction flags, specific to the AccountSet transaction type, serve the same purpose, but are discouraged:

Flag Name Hex Value Decimal Value Replaced by AccountSet Flag
tfRequireDestTag 0x00010000 65536 asfRequireDest (SetFlag)
tfOptionalDestTag 0x00020000 131072 asfRequireDest (ClearFlag)
tfRequireAuth 0x00040000 262144 asfRequireAuth (SetFlag)
tfOptionalAuth 0x00080000 524288 asfRequireAuth (ClearFlag)
tfDisallowXRP 0x00100000 1048576 asfDisallowXRP (SetFlag)
tfAllowXRP 0x00200000 2097152 asfDisallowXRP (ClearFlag)

Blocking Incoming Transactions

Incoming transactions with unclear purposes may be an inconvenience for some gateways, which would have to identify whether a mistake was made, and then potentially refund accounts or adjust balances depending on the mistake. The asfRequireDest and asfDisallowXRP flags are intended to protect users from accidentally sending funds to a gateway in a way that is unclear about the reason the funds were sent.

For example, a destination tag is typically used to identify which hosted balance should be credited when the gateway receives a payment. If the destination tag is omitted, it may be unclear which account should be credited, creating a need for refunds, among other problems. By using the asfRequireDest tag, the gateway (or any account) can ensure that every incoming payment has a destination tag, which makes it harder to send an ambiguous payment by accident.

Accounts can protect against unwanted incoming payments for non-XRP currencies simply by not creating trust lines in those currencies. Since XRP does not require trust, the asfDisallowXRP flag is used to discourage users from sending XRP to an account. However, this flag is not enforced in rippled because it could potentially cause accounts to become unusable. (If an account did not have enough XRP to send a transaction that disabled the flag, the account would be completely unusable.) Instead, client applications should disallow or discourage XRP payments to accounts with the asfDisallowXRP flag enabled.

TransferRate

TransferRate allows issuing gateways to charge users for sending funds to other users of the same gateway. It adds a fee, specified in billionths of a unit (for all non-XRP currencies) that applies when a user pays another user in the currency issued by this account. The fee "disappears" from the balances on the ledger, becoming the property of the issuing gateway. The value cannot be less than 1000000000. (Less than that would indicate giving away money for sending transactions, which is exploitable.) You can specify 0 as a shortcut for 1000000000, meaning no fee.

For example, if HighFeeGateway issues USD and sets the TransferRate to 120000000 and Norman wants to send Arthur $100 of USD issued by HighFeeGateway, Norman would have to spend $120 in order for Arthur to receive $100. The other $20 would no longer be tracked on the Ripple Ledger, and would become the property of HighFeeGateway instead.

SetRegularKey

[Source]

A SetRegularKey transaction changes the regular key used by the account to sign future transactions.

{
    "Flags": 0,
    "TransactionType": "SetRegularKey",
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
    "Fee": "12",
    "RegularKey": "rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD"
}
Field JSON Type Internal Type Description
RegularKey String Account (Optional) The public key of a new keypair, to use as the regular key to this account, as a base-58-encoded string in the same format as an account address. If omitted, removes the existing regular key.

Instead of using an account's master key to sign transactions, you can set an alternate key pair, called the "Regular Key". As long as the public key for this key pair is set in the RegularKey field of an account this way, then the secret of the Regular Key pair can be used to sign transactions. (The master secret can still be used, too, unless you set the asfDisableMaster account flag.)

A Regular Key pair is generated in the same way as any other Ripple keys (for example, with wallet_propose), but it can be changed. A Master Key pair is an intrinsic part of the account's identity (the address is derived from the master public key) so the Master Key cannot be changed. Therefore, using a Regular Key to sign transactions instead of the master key whenever possible is beneficial to security.

If your regular key is compromised, but the master key is not, you can use this method to regain control of your account. As a special feature, each account is allowed to perform SetRegularKey transaction without a transaction fee as long as the lsfPasswordSpent flag for the account is not set. To use this feature, submit a SetRegularKey transaction with a Fee value of 0, signed by the account's master key. (This way, you don't have to worry about whether the attacker has used up all the account's spare XRP.) The lsfPasswordSpent flag is automatically cleared if your account receives a payment of XRP.

OfferCreate

[Source]

An OfferCreate transaction is effectively a limit order. It defines an intent to exchange currencies, and creates an Offer node in the global ledger if not completely fulfilled when placed. Offers can be partially fulfilled.

{
    "TransactionType": "OfferCreate",
    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
    "Fee": "12",
    "Flags": 0,
    "LastLedgerSequence": 7108682,
    "Sequence": 8,
    "TakerGets": "6000000",
    "TakerPays": {
      "currency": "GKO",
      "issuer": "ruazs5h1qEsqpke88pcqnaseXdm6od2xc",
      "value": "2"
    }
}
Field JSON Type Internal Type Description
Expiration Unsigned Integer UInt32 (Optional) Time after which the offer is no longer active, in seconds since the Ripple Epoch.
OfferSequence Unsigned Integer UInt32 (Optional) The sequence number of a previous OfferCreate transaction. If specified, cancel any offer node in the ledger that was created by that transaction. It is not considered an error if the offer specified does not exist.
TakerGets Object (Non-XRP), or
String (XRP)
Amount The amount and type of currency being provided by the offer creator.
TakerPays Object (Non-XRP), or
String (XRP)
Amount The amount and type of currency being requested by the offer creator.

Lifecycle of an Offer

When an OfferCreate transaction is processed, it automatically consumes matching or crossing offers to the extent possible. (If existing offers provide a better rate than requested, the offer creator could pay less than the full TakerGets amount in order to receive the entire TakerPays amount.) If that does not completely fulfill the TakerPays amount, then the offer becomes an Offer node in the ledger. (You can use OfferCreate Flags to modify this behavior.)

An offer in the ledger can be fulfilled either by additional OfferCreate transactions that match up with the existing offers, or by Payments that use the offer to connect the payment path. Offers can be partially fulfilled and partially funded.

You can create an Offer so long as you have at least some (any positive, nonzero amount) of the currency specified by the TakerGets parameter of the offer. Any amount of that currency you have, up to the TakerGets amount, will be sold until the TakerPays amount is satisfied. An offer cannot place anyone in debt.

It is possible for an offer to become temporarily or permanently unfunded:

  • If the creator no longer has any of the TakerGets currency.
    • The offer becomes funded again when the creator obtains more of that currency.
  • If the currency required to fund the offer is held in a frozen trust line.
    • The offer becomes funded again when the trust line is no longer frozen.
  • If the creator does not have enough XRP for the reserve amount of a new trust line required by the offer. (See Offers and Trust.)
    • The offer becomes funded again when the creator obtains more XRP, or the reserve requirements decrease.
  • If the Expiration time included in the offer is before the close time of the most recently-closed ledger. (See Expiration.)

An unfunded transaction can remain on the ledger indefinitely, but it does not have any effect. The only ways an offer can be permanently removed from the ledger are:

  • It becomes fully claimed by a Payment or a matching OfferCreate transaction.
  • A subsequent OfferCancel or OfferCreate transaction explicitly cancels the offer.
  • A subsequent OfferCreate from the same account crosses the earlier offer. (In this case, the older offer is automatically canceled.)
  • An offer is found to be unfunded during transaction processing, typically because it was at the tip of the orderbook.
    • This includes cases where one side or the other of an offer is found to be closer to 0 than rippled's precision supports.
  • Note: there is a bug that can cause offers to be removed incorrectly in rare circumstances. (See RIPD-456 for status.)

Offers and Trust

The limit values of trust lines (See TrustSet) do not affect offers. In other words, you can use an offer to acquire more than the maximum amount you trust an issuing gateway to redeem.

However, possessing non-XRP balances still requires a trust line to the account issuing those balances. When an offer is taken, it automatically creates any necessary trust lines, setting their limits to 0. Because trust lines increase the reserve an account must hold, any offers that would require a new trust line also require the account to have the necessary XRP to pay the reserve for that trust line.

A trust line indicates an issuer you trust enough to accept their issuances as payment, within limits. Offers are explicit instructions to acquire certain issuances, so they are allowed to go beyond those limits.

Offer Preference

Existing offers are grouped by "quality", which is measured as the ratio between TakerGets and TakerPays. Offers with a higher quality are taken preferentially. (That is, the person accepting the offer receives as much as possible for the amount of currency they pay out.) Offers with the same quality are taken on the basis of which offer was placed in the earliest ledger version.

When offers of the same quality are placed in the same ledger version, the order in which they are taken is determined by the canonical order in which the transactions were applied to the ledger. This behavior is designed to be deterministic, efficient, and hard to game.

Expiration

Since transactions can take time to propagate and confirm, the timestamp of a ledger is used to determine offer validity. An offer only expires when its Expiration time occurs prior to the most-recently validated ledger. In other words, an offer with an Expiration field is still considered "active" if its expiration time is later than the timestamp of the most-recently validated ledger, regardless of what your local clock says.

You can determine the final disposition of an offer with an Expiration as soon as you see a fully-validated ledger with a close time equal to or greater than the expiration time.

Note: Since only new transactions can modify the ledger, an expired offer can remain on the ledger after it becomes inactive. The offer is treated as unfunded and has no effect, but it can continue to appear in results (for example, from the ledger_entry command). Later on, the expired offer can get finally deleted as a result of another transaction (such as another OfferCreate) if the server encounters it while processing.

OfferCreate Flags

Transactions of the OfferCreate type support additional values in the Flags field, as follows:

Flag Name Hex Value Decimal Value Description
tfPassive 0x00010000 65536 If enabled, the offer will not consume offers that exactly match it, and instead becomes an Offer node in the ledger. It will still consume offers that cross it.
tfImmediateOrCancel 0x00020000 131072 Treat the offer as an Immediate or Cancel order. If enabled, the offer will never become a ledger node: it only attempts to match existing offers in the ledger.
tfFillOrKill 0x00040000 262144 Treat the offer as a Fill or Kill order. Only attempt to match existing offers in the ledger, and only do so if the entire TakerPays quantity can be obtained.
tfSell 0x00080000 524288 Exchange the entire TakerGets amount, even if it means obtaining more than the TakerPays amount in exchange.

The following invalid flag combination prompts a temINVALID_FLAG error:

  • tfImmediateOrCancel and tfFillOrKill

OfferCancel

[Source]

An OfferCancel transaction removes an Offer node from the global ledger.

{
    "TransactionType": "OfferCancel",
    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
    "Fee": "12",
    "Flags": 0,
    "LastLedgerSequence": 7108629,
    "OfferSequence": 6,
    "Sequence": 7
}
Field JSON Type Internal Type Description
OfferSequence Unsigned Integer UInt32 The sequence number of the offer to cancel.

Tip: To remove an old offer and replace it with a new one, you can use an OfferCreate transaction with an OfferSequence parameter, instead of using OfferCancel and another OfferCreate.

The OfferCancel method returns tesSUCCESS even if it did not find an offer with the matching sequence number.

TrustSet

[Source]

Create or modify a trust line linking two accounts.

{
    "TransactionType": "TrustSet",
    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
    "Fee": "12",
    "Flags": 262144,
    "LastLedgerSequence": 8007750,
    "LimitAmount": {
      "currency": "USD",
      "issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",
      "value": "100"
    },
    "Sequence": 12
}
Field JSON Type Internal Type Description
LimitAmount Object Amount Object defining the trust line to create or modify, in the same format as currency amounts.
LimitAmount.currency String (Amount.currency) The currency to this trust line applies to, as a three-letter ISO 4217 Currency Code or a 160-bit hex value according to currency format. "XRP" is invalid.
LimitAmount.value String (Amount.value) Quoted decimal representation of the limit to set on this trust line.
LimitAmount.issuer String (Amount.issuer) The address of the account to extend trust to.
QualityIn Unsigned Integer UInt32 (Optional) % fee for incoming value on this line, represented as an integer over 1,000,000,000. A value of 0 is shorthand for no fee.
QualityOut Unsigned Integer UInt32 (Optional) % fee for outgoing value on this line, represented as an integer over 1,000,000,000. A value of 0 is shorthand for no fee.

Trust Limits

All balances on the Ripple Network, except for XRP, represent value owed in the world outside the Ripple Ledger. The account that issues those funds in Ripple (identified by the issuer field of the LimitAmount object) is responsible for paying the balance back, outside of the Ripple Network, when users redeem their Ripple balances by returning them to the issuing account.

Since a computer program cannot force the gateway to keep its promise and not default in real life, trust lines represent a way of configuring how much you are willing to trust the issuing account to hold on your behalf. Since a large, reputable issuing gateway is more likely to be able to pay you back than, say, your broke roommate, you can set different limits on each trust line, to indicate the maximum amount you are willing to let the issuing account "owe" you (off the network) for the funds that you hold on the network. In the case that the issuing gateway defaults or goes out of business, you can lose up to that much money because the balances you hold in the Ripple Network can no longer be exchanged for equivalent balances off the network. (The Ripple Ledger will still reflect that you possess the same balance with that issuing gateway, but you won't be able to redeem, making it unlikely to be worth anything.)

There are two cases where you can hold a balance on a trust line that is greater than your limit: when you acquire more of that currency through trading, or when you decrease the limit on your trust line.

Since a trust line occupies space in the ledger, a trust line increases the XRP your account must hold in reserve. This applies to the account extending trust, not to the account receiving it.

A trust line with a limit and a balance of 0 is equivalent to no trust line.

TrustSet Flags

Transactions of the TrustSet type support additional values in the Flags field, as follows:

Flag Name Hex Value Decimal Value Description
tfSetAuth 0x00010000 65536 Authorize the other party to hold issuances from this account. (No effect unless using the asfRequireAuth AccountSet flag.) Cannot be unset.
tfSetNoRipple 0x00020000 131072 Blocks rippling between two trustlines of the same currency, if this flag is set on both. (See No Ripple for details.)
tfClearNoRipple 0x00040000 262144 Clears the No-Rippling flag. (See No Ripple for details.)
tfSetFreeze 0x00100000 1048572 Freeze the trustline.
tfClearFreeze 0x00200000 2097152 Unfreeze the trustline.

Pseudo-Transactions

Pseudo-Transactions are never submitted by users, nor propagated through the network. Instead, a server may choose to inject them in a proposed ledger directly. If enough servers inject an equivalent pseudo-transaction for it to pass consensus, then it becomes included in the ledger, and appears in ledger data thereafter.

Some of the fields that are mandatory for normal transactions do not make sense for pseudo-transactions. In those cases, the pseudo-transaction has the following default values:

Field Default Value
Account ACCOUNT_ZERO
Sequence 0
Fee 0
SigningPubKey ""
Signature ""

Fee

A change in transaction or account fees. This is typically in response to changes in the load on the network.

Note: There have been very few, if any, Fee psuedo-transactions, ever. It is possible, but very unlikely, that you may encounter one in a historical ledger.

Field JSON Type Internal Type Description
BaseFee String (Quoted Integer) UInt64 The charge, in drops, for the reference transaction. (See Transaction Fee Terminology
ReferenceFeeUnits Unsigned Integer UInt32 The cost, in fee units, of the reference transaction
ReserveBase Unsigned Integer UInt32 The base reserve, in drops
ReserveIncrement Unsigned Integer UInt32 The incremental reserve, in drops

Transaction Results

The result of the submit command contains several fields that indicate what happened in processing the submitted transaction. These fields are as follows:

Field Value Description
engine_result String A code that categorizes the result, such as tecPATH_DRY
engine_result_code Signed Integer A number that corresponds to the engine_result, although exact values are subject to change.
engine_result_message String A human-readable message explaining what happened.

If nothing went wrong in the process of submitting and applying the transaction locally, the response looks like this:

    "engine_result": "tesSUCCESS",
    "engine_result_code": 0,
    "engine_result_message": "The transaction was applied. Only final in a validated ledger."

Note: A successful result at this stage does not indicate that the transaction has completely succeeded; only that it was successfully applied to the provisional version of the ledger kept by the local server.

To see the final result of a transaction, look at the meta.TransactionResult field that is returned as part of the response to the tx command, account_tx command, or other response from rippled. Look for "validated": true to indicate that this response uses a ledger version that has been validated by consensus.

Both the engine_result and the meta.TransactionResult use standard codes to identify the results of transactions, as follows:

Category Prefix Description
Local error tel The rippled server had an error, such as being under high load. You may get a different response if you resubmit to a different server or at a different time.
Malformed transaction tem The transaction was not valid, due to improper syntax, conflicting options, a bad signature, or something else.
Failure tef The transaction cannot be applied to the server's current (in-progress) ledger or any later one. It may have already been applied, or there may be an authorization problem.
Retry ter The transaction could not be applied, but it might be possible to apply later.
Success tes (Not an error) The transaction succeeded. This result is not final unless it appears in a validated ledger.
Claimed fee only tec The transaction did not achieve its intended purpose, but the transaction fee was charged. This result is not final unless it appears in a validated ledger.

Claimed Fee Justification

Although it may seem unfair to charge a fee 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. Charging a fee makes it harder for attackers to abuse the network with failed transactions.
  • The transaction fee is generally very small in real-world value, so it should not harm users unless they are sending large quantities of transactions.

Finality of Results

A signed transaction can be submitted to any rippled server, by anyone. The server processes the transaction and passes it on to other servers in the network according to its own logic. If enough servers apply a transaction to a ledger that the transaction passes consensus, then the transaction becomes a permanent part of the shared, validated global ledger. This can happen in two ways: Either the transaction is successful (a tes result), or the transaction fails but the fee is charged anyway (a tec result). No transaction with any other result is included in a ledger.

Transactions that failed in other ways could still succeed (or fail with a tec) and become included in later ledgers. A server might even store a temporarily-failed, signed transaction and then successfully apply it later without asking first; the signature means that the transaction is authorized to happen.

There are several ways a transaction's failure could become permanent:

  • If the transaction is malformed, failure is always permanent (unless the protocol changes to accept what was formerly considered an invalid transaction).
  • If the Sequence number of the account sending the transaction is higher than the Sequence number in the transaction, then the transaction cannot be included in any new ledger.
  • If the transaction includes a LastLedgerSequence and a ledger with a higher sequence number is validated, the transaction cannot be included in any new ledger.