Open RichardAH opened 3 years ago
Not sure if what I saw and reported privately this morning is symptomatic of this same issue, but here goes:
rHxN3R2uoSeJ7q726pH8CT2D4gcmQhJiGV
consumed an offer of raAbeTkVgXNqXyW6NYhQETsv2JyjRtkakf
i n C2BDA5927C73419CBD9624DE717A68A3F69BA270273755F3590E3E588F5227BC
that left a tiny remainder balance of -196e-16
of USD/rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq
. This offer persisted at the top of the orderbook and crossed the other side and could not be consumed until rYUZ3HYqNNd7TEzC4H7fXjANAoLzWY6ym
donated a single dollar in ledger 62933767, which then let that offer be consumed.
I do not know the causes, just that the offer being unconsumable was highly unusual.
Thanks for the bug report.
I took a look at the transaction, and it looks correct to me.
First, if we look at the transaction and its metadata, it's an OfferCreate
for TakerPays .00001USD
and TakerGets 1000 drops
. The metadata shows an offer created with those TakerPays
and TakerGets
amounts. That looks correct.
The logging data is confusing, and I can see why that might look like a bug. The reason for the misleading log messages is that offer crossing uses the payment engine as part of its implementation (we used to use a different implementation). When offer crossing runs the payment engine, it doesn't find a suitable offer to cross, and fails with All strands dry
message. But this just means there's no offer to cross, not that the offer create fails. The OfferCreate
transaction succeeds with a tesSUCCESS
even though the payment engine failed to cross the offer.
I also replayed the transaction, and there is something interesting about it. There's an offer with a TakerPays ~4XRP
, TakerGets ~8USD
. Normally that offer would cross. However, the owner only has ~10^-14USD to fund the offer. When that is taken into account, the offer becomes TakerPays 1 drop
, TakerGets ~10^-14USD
, and the offer no longer crosses and processing stops.
Please let me know if I misunderstood anything in the bug report, and thanks again for reporting!
@donovanhide Although I have not replayed that transaction, the symptoms are similar. It also explains why sending a dollar makes the offer consumable. When taking the owner funds into account, the actual offer quality with be much closer to quality when first placed on the book (instead of something like 1 drop for 10^-14
).
So the bug is that when a tiny remainder makes the calculated offer quality differ from the original offer quality, the offer may get stuck at the top of the orderbook even when an ImmediateOrCancel OfferCreate is submitted which crosses it?
When doing offer crossing we use the actual offer quality, not the quality of the original offer. And when taking owner funds into account the actual quality no longer crosses. Yes, this can cause these tiny "dust" offers to stay at the top of the order book, even when there are offers with much better actual quality below it. Ideally, a regular payment will come along and consume the offer, but it appears these offers may be sticking longer than we'd like.
The problem that I had is an OfferCreate
that crossed the dust offer didn't consume it or any offers beneath it in the quality ordering. For example BB1609F62E369DA40BE782B18BADF3419BC1876D52BD567E4D4A215E13E57F09
Waiting for a payment to come along to clear it away is far from ideal. It effectively blocks the orderbook, as could be witnessed this morning when trades on only one side of the XRP
USD/rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq
orderbook were occurring, until the deliberate payment was made.
@donovanhide Yes, I agree it's far from ideal. I recently made some changes that help with this on the payments side, but I haven't thought of a good solution for the offer create side. Given that it's becoming a real problem for people, I'll give it some more thought.
@seelabs this has just happened again with an offer of rH5WJmyWhjLpxzNWnpYaRZVGWV9jQz2Jd5
in many ledgers, including 62981379
, with a micro amount of 2195608782435129e-33
BTC/rchGBxcD1A1C2tdxF6papQYZ8kjRKMYcL
stopping offers being consumed. Don't know if this is a live exploit, or just coincidence.
Thanks @donovanhide I'm actively working on a fix now.
Issue Description
An unusual / wrong Offer node created by
3589187DEF462C8EC8B249E3A5767504F243A1C929FCF52F9092C1C9658FE111
on mainnet.Transaction:
Steps to Reproduce
Replay of ledger 62932632:
Expected Result
If the cross fails with
All strands dry
it should not go on to return as tesSUCCESS. (I'm not sure if this is the only issue here.)Actual Result
The order is "crossed" for 0 value then placed on the wrong side of the book.
Use the following:
Resuling in:
Environment
Mainnet
Quick analysis
It appears the following condition should fire but doesn't. https://github.com/ripple/rippled/blob/2913847925ec8cabde868e6502a55610a768736f/src/ripple/app/paths/impl/StrandFlow.h#L757-L760
Other notes
I tried to replicate the transaction in a later ledger (
9C947F9E6B6358EEB3B4EA8D8A494A4B8299A1CC69B45A2AD371B0D8AE646EDB
) and was unable to reproduce the behaviour suggesting it is probably not trivially exploitable