From 3451d15e122f2e7d984a871d2878c4cd51fb02bf Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Sat, 15 Nov 2025 02:55:48 -0500 Subject: [PATCH] Step 3: Automatically switch precision in the transaction engine - Default Number outside of transaction processing to be "large" so RPC will work. --- src/libxrpl/basics/Number.cpp | 3 +- src/libxrpl/protocol/Rules.cpp | 13 ++- src/xrpld/app/tx/detail/Transactor.cpp | 6 +- src/xrpld/app/tx/detail/applySteps.cpp | 111 +++++++++++++++---------- 4 files changed, 86 insertions(+), 47 deletions(-) diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 0381a8a382..6a8ba01841 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -36,9 +36,8 @@ struct make_unsigned namespace ripple { thread_local Number::rounding_mode Number::mode_ = Number::to_nearest; -// TODO: Once the Rules switching is implemented, default to largeRange thread_local std::reference_wrapper Number::range_ = - smallRange; // largeRange; + largeRange; Number::rounding_mode Number::getround() diff --git a/src/libxrpl/protocol/Rules.cpp b/src/libxrpl/protocol/Rules.cpp index 7d84c4e7da..344ed61f0b 100644 --- a/src/libxrpl/protocol/Rules.cpp +++ b/src/libxrpl/protocol/Rules.cpp @@ -1,10 +1,12 @@ +#include +// #include +#include #include #include #include #include #include -#include #include #include @@ -33,6 +35,15 @@ getCurrentTransactionRules() void setCurrentTransactionRules(std::optional r) { + // Make global changes associated with the rules before the value is moved. + // Push the appropriate setting, instead of having the class pull every time + // the value is needed. That could get expensive fast. + bool enableLargeNumbers = !r || + (r->enabled(featureSingleAssetVault) /*|| + r->enabled(featureLendingProtocol)*/); + Number::setMantissaScale( + enableLargeNumbers ? MantissaRange::large : MantissaRange::small); + *getCurrentTransactionRulesRef() = std::move(r); } diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 288ae4c85d..981dbc61c7 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -1115,6 +1115,10 @@ Transactor::operator()() { JLOG(j_.trace()) << "apply: " << ctx_.tx.getTransactionID(); + // These global updates really should have been for every Transaction + // step: preflight, preclaim, and doApply. And even calculateBaseFee. See + // with_txn_type(). + // // raii classes for the current ledger rules. // fixUniversalNumber predate the rulesGuard and should be replaced. NumberSO stNumberSO{view().rules().enabled(fixUniversalNumber)}; @@ -1131,7 +1135,7 @@ Transactor::operator()() { // LCOV_EXCL_START JLOG(j_.fatal()) << "Transaction serdes mismatch"; - JLOG(j_.info()) << to_string(ctx_.tx.getJson(JsonOptions::none)); + JLOG(j_.fatal()) << ctx_.tx.getJson(JsonOptions::none); JLOG(j_.fatal()) << s2.getJson(JsonOptions::none); UNREACHABLE( "ripple::Transactor::operator() : transaction serdes mismatch"); diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index 81a930f562..39a5c5ee90 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -34,8 +34,31 @@ struct UnknownTxnType : std::exception // throw an "UnknownTxnType" exception on error template auto -with_txn_type(TxType txnType, F&& f) +with_txn_type(Rules const& rules, TxType txnType, F&& f) { + // These global updates really should have been for every Transaction + // step: preflight, preclaim, calculateBaseFee, and doApply. Unfortunately, + // they were only included in doApply (via Transactor::operator()). That may + // have been sufficient when the changes were only related to operations + // that mutated data, but some features will now change how they read data, + // so these need to be more global. + // + // To prevent unintentional side effects on existing checks, they will be + // set for every operation only once SingleAssetVault (or later + // LendingProtocol) are enabled. + // + // See also Transactor::operator(). + // + std::optional stNumberSO; + std::optional rulesGuard; + if (rules.enabled(featureSingleAssetVault) /*|| rules.enabled(featureLendingProtocol)*/) + { + // raii classes for the current ledger rules. + // fixUniversalNumber predate the rulesGuard and should be replaced. + stNumberSO.emplace(rules.enabled(fixUniversalNumber)); + rulesGuard.emplace(rules); + } + switch (txnType) { #pragma push_macro("TRANSACTION") @@ -99,7 +122,7 @@ invoke_preflight(PreflightContext const& ctx) { try { - return with_txn_type(ctx.tx.getTxnType(), [&]() { + return with_txn_type(ctx.rules, ctx.tx.getTxnType(), [&]() { auto const tec = Transactor::invokePreflight(ctx); return std::make_pair( tec, @@ -126,50 +149,51 @@ invoke_preclaim(PreclaimContext const& ctx) { // use name hiding to accomplish compile-time polymorphism of static // class functions for Transactor and derived classes. - return with_txn_type(ctx.tx.getTxnType(), [&]() -> TER { - // preclaim functionality is divided into two sections: - // 1. Up to and including the signature check: returns NotTEC. - // All transaction checks before and including checkSign - // MUST return NotTEC, or something more restrictive. - // Allowing tec results in these steps risks theft or - // destruction of funds, as a fee will be charged before the - // signature is checked. - // 2. After the signature check: returns TER. + return with_txn_type( + ctx.view.rules(), ctx.tx.getTxnType(), [&]() -> TER { + // preclaim functionality is divided into two sections: + // 1. Up to and including the signature check: returns NotTEC. + // All transaction checks before and including checkSign + // MUST return NotTEC, or something more restrictive. + // Allowing tec results in these steps risks theft or + // destruction of funds, as a fee will be charged before the + // signature is checked. + // 2. After the signature check: returns TER. - // If the transactor requires a valid account and the - // transaction doesn't list one, preflight will have already - // a flagged a failure. - auto const id = ctx.tx.getAccountID(sfAccount); + // If the transactor requires a valid account and the + // transaction doesn't list one, preflight will have already + // a flagged a failure. + auto const id = ctx.tx.getAccountID(sfAccount); - if (id != beast::zero) - { - if (NotTEC const preSigResult = [&]() -> NotTEC { - if (NotTEC const result = - T::checkSeqProxy(ctx.view, ctx.tx, ctx.j)) - return result; + if (id != beast::zero) + { + if (NotTEC const preSigResult = [&]() -> NotTEC { + if (NotTEC const result = + T::checkSeqProxy(ctx.view, ctx.tx, ctx.j)) + return result; - if (NotTEC const result = - T::checkPriorTxAndLastLedger(ctx)) - return result; + if (NotTEC const result = + T::checkPriorTxAndLastLedger(ctx)) + return result; - if (NotTEC const result = - T::checkPermission(ctx.view, ctx.tx)) - return result; + if (NotTEC const result = + T::checkPermission(ctx.view, ctx.tx)) + return result; - if (NotTEC const result = T::checkSign(ctx)) - return result; + if (NotTEC const result = T::checkSign(ctx)) + return result; - return tesSUCCESS; - }()) - return preSigResult; + return tesSUCCESS; + }()) + return preSigResult; - if (TER const result = - T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx))) - return result; - } + if (TER const result = T::checkFee( + ctx, calculateBaseFee(ctx.view, ctx.tx))) + return result; + } - return T::preclaim(ctx); - }); + return T::preclaim(ctx); + }); } catch (UnknownTxnType const& e) { @@ -204,7 +228,7 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx) { try { - return with_txn_type(tx.getTxnType(), [&]() { + return with_txn_type(view.rules(), tx.getTxnType(), [&]() { return T::calculateBaseFee(view, tx); }); } @@ -264,10 +288,11 @@ invoke_apply(ApplyContext& ctx) { try { - return with_txn_type(ctx.tx.getTxnType(), [&]() { - T p(ctx); - return p(); - }); + return with_txn_type( + ctx.view().rules(), ctx.tx.getTxnType(), [&]() { + T p(ctx); + return p(); + }); } catch (UnknownTxnType const& e) {