fix: Serialize MPToken UInt64 amounts as base-10 strings#3139
fix: Serialize MPToken UInt64 amounts as base-10 strings#3139ckeshava wants to merge 13 commits into
Conversation
…ision of very large numbers (greater than 2^53). This commit returns a string representation instead of Number representation in the RPC outputs to solve this issue.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
/ai-reviewer |
| // UInt64 amount fields must be serialized as base-10 strings (matching rippled's | ||
| // STUInt64::getJson) so that JSON parsers using IEEE-754 doubles do not silently lose | ||
| // precision for values greater than 2^53. | ||
| auto const uint64ToString = [](xrpl::SField const& field, std::uint64_t value) { |
There was a problem hiding this comment.
You don't need to do this this PR, but I think this function along with setUint64IfPresent and in MPTHolders all do the same thing more or less. If this mpt_amount ever changes, there will be 3 places that needs to change. I think worth adding a helper to RPChelpers.cpp: something like:
boost::json::string
toBoostJsonString(xrpl::SField const& field, std::uint64_t value);
And all these 3 places can use it instead
There was a problem hiding this comment.
yes, that makes sense. its good to have one common utility method.
There was a problem hiding this comment.
Based on my search, I found only two similar utility methods: setUint64IfPresent (introduced in this PR) and the verbose code in MPTHolders RPC, both of which do identical tasks. I wanted to keep the diff of this PR as limited as possible, but it is a good idea to refactor them if there are more such usages.
PeterChen13579
left a comment
There was a problem hiding this comment.
LGTM, just left one comment
godexsoft
left a comment
There was a problem hiding this comment.
This PR seems to do what is needed. Let's just polish a bit and don't use obsolete names 👍
| ValuesIn( | ||
| std::vector<AccountMPTokensAmountSerializationTestCaseBundle>{ | ||
| {.testName = "LargeAmounts", | ||
| .mptAmount = 9223372036854775807ULL, // 2^63 - 1 (max MPT amount) |
There was a problem hiding this comment.
So for this you used (1ULL << 63) - 1 and stuff like that. It of course works but it's even less readable now 😃
What i meant is use std::pow(2, 63) - 1. Note that they are constexpr only from c++26 so right now this would be a runtime call. Which is fine in tests and will become compile time for free when c++26 is used. Readability is more important than performance in tests.
There was a problem hiding this comment.
that makes sense, I have updated this code in 8be75fb
…into updateAmtReprToStr
godexsoft
left a comment
There was a problem hiding this comment.
Let's try std::pow etc., otherwise looks good 👍
Summary
The
account_mptokensandaccount_mpt_issuancesRPCs emitted MPToken amount fields as JSON numbers. Because many JSON parsers back numbers with IEEE-754 doubles, any value greater than 2^53 was silently rounded, so clients could not recover the exact on-ledger amount.This change serializes the affected
UInt64amount fields as base-10 JSON strings, matching rippled'sSTUInt64::getJsonbehavior so Clio's output is consistent with the reference implementation:account_mptokens:mpt_amount,locked_amountaccount_mpt_issuances:maximum_amount,outstanding_amount,locked_amountChanges
UInt64amount fields viaxrpl::STUInt64{field, value}.getJson(...)instead of inserting the raw integer into the JSON object, so the encoding matches rippled's string representation exactly.2^63 - 1,2^53 + 1, and an odd value> 2^53) round-trip as exact strings.Test plan
RPCAccountMPTokensHandlerTest.LargeAmountsSerializedAsStringsandRPCAccountMPTokenIssuancesHandlerTest.LargeAmountsSerializedAsStrings.Notes
mpt_holdersalready serializesmpt_amountcorrectly viaSTUInt64::getJson; all other handlers that expose ledgerUInt64fields (e.g.vault_info,get_aggregate_price,gateway_balances) route through rippled's own serialization orSTAmount, so they were already emitting strings and are unaffected.🤖 Generated with Claude Code