Serguey Asael Shinder: A dry run must be priced like the real run, as rippled's simulate RPC shows
A fix merged into rippled on 4 November is a small example of a common mistake. The XRP Ledger server's simulate method autofills a transaction, parses it, copies the open ledger view and runs the transaction through the full apply logic, then throws the result away. It had been charged to the server's resource manager at the medium level used for submitting an already-signed blob, which only deserialises and queues. Pull request 6841 moves it to the heavy level already used for sign, and its description calls the old charge a partial denial-of-service risk, because rate limiting counts charges, not CPU. (https://github.com/XRPLF/rippled/pull/6841)
The pattern shows up wherever software offers a preview. A "validate" endpoint that runs every rule, a "what if" query that builds the full plan, a --dry-run flag that computes the whole diff, a test-charge call to a payment API: each does the expensive part of the real operation and skips only the cheap commit at the end. Because nothing changes in the system, it feels like a read, and reads tend to be priced low.

That intuition is backwards. The cost of a request is the work it does, not its side effects. A simulation endpoint is useful precisely because it is faithful, which makes it at least as expensive as the real call, and often more attractive to abuse, since it can be repeated without consequences.
Three habits follow. Price a preview at the cost of the operation it previews, not at the cost of a lookup. When you add a dry-run mode, make it go through the same quota or rate-limit path as the real one, so they cannot drift apart. And when you review limits, sort endpoints by the work they do, not by whether they write: the rippled bug sat in a single line that set the load type, and it had been there since the endpoint was introduced.