The XRP Ledger utilizes an account model. Unlike systems based on a UTXO
model, XRP Ledger accounts are first-class objects. This design choice
allows the XRP Ledger to offer rich functionality, including the ability
to own objects (offers, escrows, checks, signer lists) as well as other
advanced features, such as key rotation and configurable multi-signing
without needing to change a destination address.
The trade-off is that accounts must be stored on ledger. The XRP Ledger
applies reserve requirements, in XRP, to protect the shared global ledger
from growing excessively large as the result of spam or malicious usage.
Prior to this commit, accounts had been permanent objects; once created,
they could never be deleted.
This commit introduces a new amendment "DeletableAccounts" which, if
enabled, will allow account objects to be deleted by executing the new
"AccountDelete" transaction. Any funds remaining in the account will
be transferred to an account specified in the deletion transaction.
The amendment changes the mechanics of account creation; previously
a new account would have an initial sequence number of 1. Accounts
created after the amendment will have an initial sequence number that
is equal to the ledger in which the account was created.
Accounts can only be deleted if they are not associated with any
obligations (like RippleStates, Escrows, or PayChannels) and if the
current ledger sequence number exceeds the account's sequence number
by at least 256 so that, if recreated, the account can be protected
from transaction replay.
The six different ranges of TER codes are broken up into six
different enumerations. A template class allows subsets of
these enumerations to be aggregated. This technique allows
verification at compile time that no TEC codes are returned
before the signature is checked.
Conversion between TER instance and integer is provided by
named functions. This makes accidental conversion almost
impossible and makes type abuse easier to spot in the code
base.
Introduce a new ledger type: ltCHECK
Introduce three new transactions that operate on checks:
- "CheckCreate" which adds the check entry to the ledger. The
check is a promise from the source of the check that the
destination of the check may cash the check and receive up to
the SendMax specified on the check. The check may have an
expiration, after which the check may no longer be cashed.
- "CheckCash" is a request by the destination of the check to
transfer a requested amount of funds, up to the check's SendMax,
from the source to the destination. The destination may receive
less than the SendMax due to transfer fees.
When cashing a check, the destination specifies the smallest
amount of funds that will be acceptable. If the transfer
completes and delivers the requested amount, then the check is
considered cashed and removed from the ledger. If enough funds
cannot be delivered, then the transaction fails and the check
remains in the ledger.
Attempting to cash the check after its expiration will fail.
- "CheckCancel" removes the check from the ledger without
transferring funds. Either the check's source or destination
can cancel the check at any time. After a check has expired,
any account can cancel the check.
Facilities related to checks are on the "Checks" amendment.
This commit introduces the "SortedDirectories" amendment, which
addresses two distinct issues:
First, it corrects a technical flaw that could, in some edge cases,
prevent an empty intermediate page from being deleted.
Second, it sorts directory entries within a page (other than order
book page entries, which remain strictly FIFO). This makes insert
operations deterministic, instead of pseudo-random and reliant on
temporal ordering.
Lastly, it removes the ability to perform a "soft delete" where
the page number of the item to delete need not be known if the
item is in the first 20 pages, and enforces a maximum limit to
the number of pages that a directory can span.
Replace Taker.cpp with calls to the payment flow() code.
This change required a number of tweaks in the payment flow code.
These tweaks are conditionalized on whether or not offer crossing
is taking place. The flag is explicitly passed as a parameter to
the flow code.
For testing, a class was added that identifies differences in the
contents of two PaymentSandboxes. That code may be reusable in
the future.
None of the Taker offer crossing code is removed. Both versions
of the code are co-resident to support an amendment cut-over.
The code that identifies differences between Taker and Flow offer
crossing is enabled by a feature. That makes it easy to enable
or disable difference logging by changing the config file. This
approach models what was done with the payment flow code. The
differencing code should never be enabled on a production server.
Extensive offer crossing unit tests are added to examine and
verify the behavior of corner cases. The tests are currently
configured to run against both Taker and Flow offer crossing.
This gives us confidence that most cases run identically and
some of the (few) differences in behavior are documented.
If the mantissas of two non-native amounts differ by less than 10, then
subtracting them leaves a result of zero. This can cause situations
where `a>b`, yet `a-b == 0`.
One consequence of this is unfunded offers were incorrectly left in
order books. The code would check if the offer would be
consumed (`amount in offer > amount needed`), assume it wouldn't be,
yet when `amount needed` was subtracted from `amount in offer` the
result was zero and the offer was unfunded. This unfunded offer
incorrectly remained on the order book.
This patch fixes this bug.
The Ripple protocol represent transfer rates and trust line
qualities as fractions of one billion. For example, a transfer
rate of 1% is represented as 1010000000.
Previously, such rates where represented either as std::uint32_t
or std::uint64_t. Other, nominally related types, also used an
integral representation and could be unintentionally substituted.
The new Rate class addresses this by providing a simple, type
safe alternative which also helps make the code self-documenting
since arithmetic operations now can be clearly understood to
involve the scaling of an amount by a rate.
The Owner count could decrease while evaluating a strand, causing
different behavior in forward passes and reverses passes. The fix treats
a decreased owner count like a deferred credit.
In some situations, deferred credits could cause an XRP balance to be
calculated as negative, triggering some asserts.
When XRP is used as a bridge currency, a path could be falsely marked as
dry. This happens when the XRP/XXX offer recursively checks the XXX/XRP
offer and the XXX/XRP offer could not satisfy the request in a single
call.
With a single strand and limit quality the old payment code incorrectly
computed with multiquailty set to true. This could cause the total
quality to go below the requested quality even if there was liquidity
available above the requested quality value.
Before this change, the deferred credits algorithm took the current
balance and subtracted the recorded credits. Conceptually, this is the
same as taking the original balance, adding all the credits,
subtracting all the debits, and subtracting all the credits. The new
algorithm records the original balance and subtracts the debits. This
prevents errors that occur when the original balance and the recorded
credits have large differences in magnitude.
Additionally, XRP credits were recorded incorrectly in the deferred
credits table (the line was between the sender and receiver, rather than
the root account).
Replace Journal public data members with member function accessors
in order to make Journal lighter weight. The change makes a
Journal cheaper to pass by value.
Also add missing stream checks (e.g., calls to JLOG) to avoid
text processing that ultimately will not be stored in the log.
Add a new algorithm for finding the liquidity in a payment path. There
is still a reverse and forward pass, but the forward pass starts at the
limiting step rather than the payment source. This insures the limiting
step is completely consumed rather than potentially leaving a 'dust'
amount in the forward pass.
Each step in a payment is either a book step, a direct step (account to
account step), or an xrp endpoint. Each step in the existing
implementation is a triple, where each element in the triple is either
an account of a book, for a total of eight step types.
Since accounts are considered in pairs, rather than triples, transfer
fees are handled differently. In V1 of payments, in the payment path
A -> gw ->B, if A redeems to gw, and gw issues to B, a transfer fee is
changed. In the new code, a transfer fee is changed even if A issues to
gw.
* Consider ledgers incompatible based on last valid ledger
* Test against even ledgers not acquired yet
* Don't validate an incompatible ledger
* Don't switch to an incompatible ledger
* Protect against an unreasonably small quorum