mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-12 15:02:32 +00:00
Compare commits
6 Commits
mvadari/pa
...
ximinez/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9076cbdd1f | ||
|
|
e2e537b3bb | ||
|
|
a873250019 | ||
|
|
e003a57f07 | ||
|
|
56c9d1d497 | ||
|
|
d52dd29d20 |
16
.github/workflows/reusable-build-test-config.yml
vendored
16
.github/workflows/reusable-build-test-config.yml
vendored
@@ -76,7 +76,7 @@ jobs:
|
||||
name: ${{ inputs.config_name }}
|
||||
runs-on: ${{ fromJSON(inputs.runs_on) }}
|
||||
container: ${{ inputs.image != '' && inputs.image || null }}
|
||||
timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 60 }}
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
# Use a namespace to keep the objects separate for each configuration.
|
||||
CCACHE_NAMESPACE: ${{ inputs.config_name }}
|
||||
@@ -227,17 +227,11 @@ jobs:
|
||||
|
||||
- name: Set sanitizer options
|
||||
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
|
||||
env:
|
||||
CONFIG_NAME: ${{ inputs.config_name }}
|
||||
run: |
|
||||
ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp"
|
||||
if [[ "${CONFIG_NAME}" == *gcc* ]]; then
|
||||
ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0"
|
||||
fi
|
||||
echo "ASAN_OPTIONS=${ASAN_OPTS}" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
echo "ASAN_OPTIONS=print_stacktrace=1:detect_container_overflow=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=second_deadlock_stack=1:halt_on_error=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Run the separate tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
|
||||
@@ -17,7 +17,6 @@ repos:
|
||||
args: [--maxkb=400, --enforce-all]
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
- id: check-merge-conflict
|
||||
args: [--assume-in-merge]
|
||||
|
||||
@@ -38,6 +37,7 @@ repos:
|
||||
rev: c2bc67fe8f8f549cc489e00ba8b45aa18ee713b1 # frozen: v3.8.1
|
||||
hooks:
|
||||
- id: prettier
|
||||
args: [--end-of-line=auto]
|
||||
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0
|
||||
|
||||
@@ -23,7 +23,7 @@ target_compile_definitions(
|
||||
BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
>
|
||||
$<$<NOT:$<BOOL:${boost_show_deprecated}>>:
|
||||
BOOST_COROUTINES2_NO_DEPRECATION_WARNING
|
||||
BOOST_COROUTINES_NO_DEPRECATION_WARNING
|
||||
BOOST_BEAST_ALLOW_DEPRECATED
|
||||
BOOST_FILESYSTEM_DEPRECATED
|
||||
>
|
||||
|
||||
@@ -7,7 +7,7 @@ find_package(
|
||||
COMPONENTS
|
||||
chrono
|
||||
container
|
||||
context
|
||||
coroutine
|
||||
date_time
|
||||
filesystem
|
||||
json
|
||||
@@ -26,7 +26,7 @@ target_link_libraries(
|
||||
Boost::headers
|
||||
Boost::chrono
|
||||
Boost::container
|
||||
Boost::context
|
||||
Boost::coroutine
|
||||
Boost::date_time
|
||||
Boost::filesystem
|
||||
Boost::json
|
||||
@@ -38,26 +38,23 @@ target_link_libraries(
|
||||
if(Boost_COMPILER)
|
||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||
endif()
|
||||
|
||||
# GCC 14+ has a false positive -Wuninitialized warning in Boost.Coroutine2's
|
||||
# state.hpp when compiled with -O3. This is due to GCC's intentional behavior
|
||||
# change (Bug #98871, #119388) where warnings from inlined system header code
|
||||
# are no longer suppressed by -isystem. The warning occurs in operator|= in
|
||||
# boost/coroutine2/detail/state.hpp when inlined from push_control_block::destroy().
|
||||
# See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119388
|
||||
if(is_gcc AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)
|
||||
target_compile_options(xrpl_boost INTERFACE -Wno-uninitialized)
|
||||
endif()
|
||||
|
||||
# Boost.Context's ucontext backend has ASAN fiber-switching annotations
|
||||
# (start/finish_switch_fiber) that are compiled in when BOOST_USE_ASAN is defined.
|
||||
# This tells ASAN about coroutine stack switches, preventing false positive
|
||||
# stack-use-after-scope errors. BOOST_USE_UCONTEXT ensures the ucontext backend
|
||||
# is selected (fcontext does not support ASAN annotations).
|
||||
# These defines must match what Boost was compiled with (see conan/profiles/sanitizers).
|
||||
if(enable_asan)
|
||||
target_compile_definitions(
|
||||
xrpl_boost
|
||||
INTERFACE BOOST_USE_ASAN BOOST_USE_UCONTEXT
|
||||
if(SANITIZERS_ENABLED AND is_clang)
|
||||
# TODO: gcc does not support -fsanitize-blacklist...can we do something else for gcc ?
|
||||
if(NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||
get_target_property(
|
||||
Boost_INCLUDE_DIRS
|
||||
Boost::headers
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
)
|
||||
endif()
|
||||
message(STATUS "Adding [${Boost_INCLUDE_DIRS}] to sanitizer blacklist")
|
||||
file(
|
||||
WRITE ${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt
|
||||
"src:${Boost_INCLUDE_DIRS}/*"
|
||||
)
|
||||
target_compile_options(
|
||||
opts
|
||||
INTERFACE # ignore boost headers for sanitizing
|
||||
-fsanitize-blacklist=${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -7,21 +7,16 @@ include(default)
|
||||
{% if compiler == "gcc" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set defines = [] %}
|
||||
{% set model_code = "" %}
|
||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1", "-Wno-stringop-overflow"] %}
|
||||
|
||||
{% if "address" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("address") %}
|
||||
{% set model_code = "-mcmodel=large" %}
|
||||
{% set _ = defines.append("BOOST_USE_ASAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% set model_code = "-mcmodel=medium" %}
|
||||
{% set _ = extra_cxxflags.append("-Wno-tsan") %}
|
||||
{% set _ = defines.append("BOOST_USE_TSAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
@@ -34,22 +29,16 @@ include(default)
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:defines+={{defines}}
|
||||
{% endif %}
|
||||
{% elif compiler == "apple-clang" or compiler == "clang" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set defines = [] %}
|
||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1"] %}
|
||||
|
||||
{% if "address" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("address") %}
|
||||
{% set _ = defines.append("BOOST_USE_ASAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% set _ = defines.append("BOOST_USE_TSAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
@@ -63,24 +52,8 @@ include(default)
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:defines+={{defines}}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"]
|
||||
|
||||
[options]
|
||||
{% if sanitizers %}
|
||||
{% if "address" in sanitizers %}
|
||||
# Build Boost.Context with ucontext backend (not fcontext) so that
|
||||
# ASAN fiber-switching annotations (__sanitizer_start/finish_switch_fiber)
|
||||
# are compiled into the library. fcontext (assembly) has no ASAN support.
|
||||
# define=BOOST_USE_ASAN=1 is critical: it must be defined when building
|
||||
# Boost.Context itself so the ucontext backend compiles in the ASAN annotations.
|
||||
boost/*:extra_b2_flags=context-impl=ucontext address-sanitizer=on define=BOOST_USE_ASAN=1
|
||||
boost/*:without_context=False
|
||||
# Boost stacktrace fails to build with some sanitizers
|
||||
boost/*:without_stacktrace=True
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags"]
|
||||
|
||||
12
conanfile.py
12
conanfile.py
@@ -1,4 +1,3 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
@@ -57,9 +56,6 @@ class Xrpl(ConanFile):
|
||||
"tests": False,
|
||||
"unity": False,
|
||||
"xrpld": False,
|
||||
"boost/*:without_context": False,
|
||||
"boost/*:without_coroutine": True,
|
||||
"boost/*:without_coroutine2": False,
|
||||
"date/*:header_only": True,
|
||||
"ed25519/*:shared": False,
|
||||
"grpc/*:shared": False,
|
||||
@@ -129,12 +125,6 @@ class Xrpl(ConanFile):
|
||||
if self.settings.compiler in ["clang", "gcc"]:
|
||||
self.options["boost"].without_cobalt = True
|
||||
|
||||
# Check if environment variable exists
|
||||
if "SANITIZERS" in os.environ:
|
||||
sanitizers = os.environ["SANITIZERS"]
|
||||
if "address" in sanitizers.lower():
|
||||
self.default_options["fPIC"] = False
|
||||
|
||||
def requirements(self):
|
||||
self.requires("boost/1.90.0", force=True, transitive_headers=True)
|
||||
self.requires("date/3.0.4", transitive_headers=True)
|
||||
@@ -201,7 +191,7 @@ class Xrpl(ConanFile):
|
||||
"boost::headers",
|
||||
"boost::chrono",
|
||||
"boost::container",
|
||||
"boost::context",
|
||||
"boost::coroutine",
|
||||
"boost::date_time",
|
||||
"boost::filesystem",
|
||||
"boost::json",
|
||||
|
||||
@@ -104,7 +104,6 @@ words:
|
||||
- endmacro
|
||||
- exceptioned
|
||||
- Falco
|
||||
- fcontext
|
||||
- finalizers
|
||||
- firewalled
|
||||
- fmtdur
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class F>
|
||||
@@ -8,18 +10,16 @@ JobQueue::Coro::Coro(Coro_create_t, JobQueue& jq, JobType type, std::string cons
|
||||
, type_(type)
|
||||
, name_(name)
|
||||
, coro_(
|
||||
// Stack size of 1MB wasn't sufficient for deep calls. ASAN tests flagged the issue. Hence
|
||||
// increasing the size to 1.5MB.
|
||||
boost::context::protected_fixedsize_stack(1536 * 1024),
|
||||
[this, fn = std::forward<F>(f)](
|
||||
boost::coroutines2::asymmetric_coroutine<void>::push_type& do_yield) {
|
||||
boost::coroutines::asymmetric_coroutine<void>::push_type& do_yield) {
|
||||
yield_ = &do_yield;
|
||||
yield();
|
||||
fn(shared_from_this());
|
||||
#ifndef NDEBUG
|
||||
finished_ = true;
|
||||
#endif
|
||||
})
|
||||
},
|
||||
boost::coroutines::attributes(megabytes(1)))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#include <xrpl/core/detail/Workers.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <boost/context/protected_fixedsize_stack.hpp>
|
||||
#include <boost/coroutine2/all.hpp>
|
||||
#include <boost/coroutine/all.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -49,8 +48,8 @@ public:
|
||||
std::mutex mutex_;
|
||||
std::mutex mutex_run_;
|
||||
std::condition_variable cv_;
|
||||
boost::coroutines2::coroutine<void>::pull_type coro_;
|
||||
boost::coroutines2::coroutine<void>::push_type* yield_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::pull_type coro_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::push_type* yield_;
|
||||
#ifndef NDEBUG
|
||||
bool finished_ = false;
|
||||
#endif
|
||||
|
||||
@@ -20,8 +20,9 @@ private:
|
||||
struct ValueIOU
|
||||
{
|
||||
explicit ValueIOU() = default;
|
||||
STAmount lowAcctDebits;
|
||||
STAmount highAcctDebits;
|
||||
|
||||
STAmount lowAcctCredits;
|
||||
STAmount highAcctCredits;
|
||||
STAmount lowAcctOrigBalance;
|
||||
};
|
||||
|
||||
@@ -228,6 +229,13 @@ public:
|
||||
apply(PaymentSandbox& to);
|
||||
/** @} */
|
||||
|
||||
// Return a map of balance changes on trust lines. The low account is the
|
||||
// first account in the key. If the two accounts are equal, the map contains
|
||||
// the total changes in currency regardless of issuer. This is useful to get
|
||||
// the total change in XRP balances.
|
||||
std::map<std::tuple<AccountID, AccountID, Currency>, STAmount>
|
||||
balanceChanges(ReadView const& view) const;
|
||||
|
||||
XRPAmount
|
||||
xrpDestroyed() const;
|
||||
|
||||
|
||||
@@ -344,10 +344,6 @@ enum TECcodes : TERUnderlyingType {
|
||||
tecLIMIT_EXCEEDED = 195,
|
||||
tecPSEUDO_ACCOUNT = 196,
|
||||
tecPRECISION_LOSS = 197,
|
||||
// DEPRECATED: This error code tecNO_DELEGATE_PERMISSION is reserved for
|
||||
// backward compatibility with historical data on non-prod networks, can be
|
||||
// reclaimed after those networks reset.
|
||||
tecNO_DELEGATE_PERMISSION = 198,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -312,6 +312,29 @@ writeDiffs(std::ostringstream& ostr, Iter begin, Iter end)
|
||||
ostr << ']';
|
||||
};
|
||||
|
||||
using BalanceDiffs =
|
||||
std::pair<std::map<std::tuple<AccountID, AccountID, Currency>, STAmount>, XRPAmount>;
|
||||
|
||||
inline BalanceDiffs
|
||||
balanceDiffs(PaymentSandbox const& sb, ReadView const& rv)
|
||||
{
|
||||
return {sb.balanceChanges(rv), sb.xrpDestroyed()};
|
||||
}
|
||||
|
||||
inline std::string
|
||||
balanceDiffsToString(std::optional<BalanceDiffs> const& bd)
|
||||
{
|
||||
if (!bd)
|
||||
return std::string{};
|
||||
auto const& diffs = bd->first;
|
||||
auto const& xrpDestroyed = bd->second;
|
||||
std::ostringstream ostr;
|
||||
ostr << ", xrpDestroyed: " << to_string(xrpDestroyed);
|
||||
ostr << ", balanceDiffs: ";
|
||||
writeDiffs(ostr, diffs.begin(), diffs.end());
|
||||
return ostr.str();
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace path
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -505,6 +505,14 @@ public:
|
||||
{
|
||||
return cur_.size();
|
||||
}
|
||||
|
||||
void
|
||||
removeIndex(std::size_t i)
|
||||
{
|
||||
if (i >= next_.size())
|
||||
return;
|
||||
next_.erase(next_.begin() + i);
|
||||
}
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
@@ -629,6 +637,11 @@ flow(
|
||||
std::optional<BestStrand> best;
|
||||
if (flowDebugInfo)
|
||||
flowDebugInfo->newLiquidityPass();
|
||||
// Index of strand to mark as inactive (remove from the active list) if
|
||||
// the liquidity is used. This is used for strands that consume too many
|
||||
// offers Constructed as `false,0` to workaround a gcc warning about
|
||||
// uninitialized variables
|
||||
std::optional<std::size_t> markInactiveOnUse;
|
||||
for (size_t strandIndex = 0, sie = activeStrands.size(); strandIndex != sie; ++strandIndex)
|
||||
{
|
||||
Strand const* strand = activeStrands.get(strandIndex);
|
||||
@@ -692,6 +705,11 @@ flow(
|
||||
|
||||
if (best)
|
||||
{
|
||||
if (markInactiveOnUse)
|
||||
{
|
||||
activeStrands.removeIndex(*markInactiveOnUse);
|
||||
markInactiveOnUse.reset();
|
||||
}
|
||||
savedIns.insert(best->in);
|
||||
savedOuts.insert(best->out);
|
||||
remainingOut = outReq - sum(savedOuts);
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppressions.
|
||||
#
|
||||
# ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=sanitizers/suppressions/asan.supp:halt_on_error=0"
|
||||
#
|
||||
# The detect_container_overflow=0 option disables false positives from:
|
||||
# - Boost intrusive containers (slist_iterator.hpp, hashtable.hpp, aged_unordered_container.h)
|
||||
# - Boost context/coroutine stack switching (Workers.cpp, thread.h)
|
||||
#
|
||||
# See: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
|
||||
|
||||
# Boost
|
||||
interceptor_name:boost/asio
|
||||
|
||||
# Suppress false positive stack-buffer errors in thread stack allocation
|
||||
# Related to ASan's __asan_handle_no_return warnings (github.com/google/sanitizers/issues/189)
|
||||
|
||||
@@ -41,14 +41,14 @@ DeferredCredits::creditIOU(
|
||||
|
||||
if (sender < receiver)
|
||||
{
|
||||
v.lowAcctDebits = amount;
|
||||
v.highAcctDebits = amount.zeroed();
|
||||
v.highAcctCredits = amount;
|
||||
v.lowAcctCredits = amount.zeroed();
|
||||
v.lowAcctOrigBalance = preCreditSenderBalance;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.lowAcctDebits = amount.zeroed();
|
||||
v.highAcctDebits = amount;
|
||||
v.highAcctCredits = amount.zeroed();
|
||||
v.lowAcctCredits = amount;
|
||||
v.lowAcctOrigBalance = -preCreditSenderBalance;
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ DeferredCredits::creditIOU(
|
||||
auto& v = i->second;
|
||||
if (sender < receiver)
|
||||
{
|
||||
v.lowAcctDebits += amount;
|
||||
v.highAcctCredits += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.highAcctDebits += amount;
|
||||
v.lowAcctCredits += amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,11 +195,11 @@ DeferredCredits::adjustmentsIOU(
|
||||
|
||||
if (main < other)
|
||||
{
|
||||
result.emplace(v.lowAcctDebits, v.highAcctDebits, v.lowAcctOrigBalance);
|
||||
result.emplace(v.highAcctCredits, v.lowAcctCredits, v.lowAcctOrigBalance);
|
||||
return result;
|
||||
}
|
||||
|
||||
result.emplace(v.highAcctDebits, v.lowAcctDebits, -v.lowAcctOrigBalance);
|
||||
result.emplace(v.lowAcctCredits, v.highAcctCredits, -v.lowAcctOrigBalance);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -222,8 +222,8 @@ DeferredCredits::apply(DeferredCredits& to)
|
||||
{
|
||||
auto& toVal = r.first->second;
|
||||
auto const& fromVal = i.second;
|
||||
toVal.lowAcctDebits += fromVal.lowAcctDebits;
|
||||
toVal.highAcctDebits += fromVal.highAcctDebits;
|
||||
toVal.lowAcctCredits += fromVal.lowAcctCredits;
|
||||
toVal.highAcctCredits += fromVal.highAcctCredits;
|
||||
// Do not update the orig balance, it's already correct
|
||||
}
|
||||
}
|
||||
@@ -450,6 +450,131 @@ PaymentSandbox::apply(PaymentSandbox& to)
|
||||
tab_.apply(to.tab_);
|
||||
}
|
||||
|
||||
std::map<std::tuple<AccountID, AccountID, Currency>, STAmount>
|
||||
PaymentSandbox::balanceChanges(ReadView const& view) const
|
||||
{
|
||||
using key_t = std::tuple<AccountID, AccountID, Currency>;
|
||||
// Map of delta trust lines. As a special case, when both ends of the trust
|
||||
// line are the same currency, then it's delta currency for that issuer. To
|
||||
// get the change in XRP balance, Account == root, issuer == root, currency
|
||||
// == XRP
|
||||
std::map<key_t, STAmount> result;
|
||||
|
||||
// populate a dictionary with low/high/currency/delta. This can be
|
||||
// compared with the other versions payment code.
|
||||
auto each = [&result](
|
||||
uint256 const& key,
|
||||
bool isDelete,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after) {
|
||||
STAmount oldBalance;
|
||||
STAmount newBalance;
|
||||
AccountID lowID;
|
||||
AccountID highID;
|
||||
|
||||
// before is read from prev view
|
||||
if (isDelete)
|
||||
{
|
||||
if (!before)
|
||||
return;
|
||||
|
||||
auto const bt = before->getType();
|
||||
switch (bt)
|
||||
{
|
||||
case ltACCOUNT_ROOT:
|
||||
lowID = xrpAccount();
|
||||
highID = (*before)[sfAccount];
|
||||
oldBalance = (*before)[sfBalance];
|
||||
newBalance = oldBalance.zeroed();
|
||||
break;
|
||||
case ltRIPPLE_STATE:
|
||||
lowID = (*before)[sfLowLimit].getIssuer();
|
||||
highID = (*before)[sfHighLimit].getIssuer();
|
||||
oldBalance = (*before)[sfBalance];
|
||||
newBalance = oldBalance.zeroed();
|
||||
break;
|
||||
case ltOFFER:
|
||||
// TBD
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!before)
|
||||
{
|
||||
// insert
|
||||
auto const at = after->getType();
|
||||
switch (at)
|
||||
{
|
||||
case ltACCOUNT_ROOT:
|
||||
lowID = xrpAccount();
|
||||
highID = (*after)[sfAccount];
|
||||
newBalance = (*after)[sfBalance];
|
||||
oldBalance = newBalance.zeroed();
|
||||
break;
|
||||
case ltRIPPLE_STATE:
|
||||
lowID = (*after)[sfLowLimit].getIssuer();
|
||||
highID = (*after)[sfHighLimit].getIssuer();
|
||||
newBalance = (*after)[sfBalance];
|
||||
oldBalance = newBalance.zeroed();
|
||||
break;
|
||||
case ltOFFER:
|
||||
// TBD
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// modify
|
||||
auto const at = after->getType();
|
||||
XRPL_ASSERT(
|
||||
at == before->getType(),
|
||||
"xrpl::PaymentSandbox::balanceChanges : after and before "
|
||||
"types matching");
|
||||
switch (at)
|
||||
{
|
||||
case ltACCOUNT_ROOT:
|
||||
lowID = xrpAccount();
|
||||
highID = (*after)[sfAccount];
|
||||
oldBalance = (*before)[sfBalance];
|
||||
newBalance = (*after)[sfBalance];
|
||||
break;
|
||||
case ltRIPPLE_STATE:
|
||||
lowID = (*after)[sfLowLimit].getIssuer();
|
||||
highID = (*after)[sfHighLimit].getIssuer();
|
||||
oldBalance = (*before)[sfBalance];
|
||||
newBalance = (*after)[sfBalance];
|
||||
break;
|
||||
case ltOFFER:
|
||||
// TBD
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The following are now set, put them in the map
|
||||
auto delta = newBalance - oldBalance;
|
||||
auto const cur = newBalance.get<Issue>().currency;
|
||||
result[std::make_tuple(lowID, highID, cur)] = delta;
|
||||
auto r = result.emplace(std::make_tuple(lowID, lowID, cur), delta);
|
||||
if (r.second)
|
||||
{
|
||||
r.first->second += delta;
|
||||
}
|
||||
|
||||
delta.negate();
|
||||
r = result.emplace(std::make_tuple(highID, highID, cur), delta);
|
||||
if (r.second)
|
||||
{
|
||||
r.first->second += delta;
|
||||
}
|
||||
};
|
||||
items_.visit(view, each);
|
||||
return result;
|
||||
}
|
||||
|
||||
XRPAmount
|
||||
PaymentSandbox::xrpDestroyed() const
|
||||
{
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
|
||||
@@ -70,11 +69,7 @@ transferRate(ReadView const& view, MPTID const& issuanceID)
|
||||
// which represents 50% of 1,000,000,000
|
||||
if (auto const sle = view.read(keylet::mptIssuance(issuanceID));
|
||||
sle && sle->isFieldPresent(sfTransferFee))
|
||||
{
|
||||
auto const fee = sle->getFieldU16(sfTransferFee);
|
||||
XRPL_ASSERT(fee <= maxTransferFee, "xrpl::transferRate : fee is too large");
|
||||
return Rate{1'000'000'000u + (10'000 * fee)};
|
||||
}
|
||||
return Rate{1'000'000'000u + (10'000 * sle->getFieldU16(sfTransferFee))};
|
||||
|
||||
return parityRate;
|
||||
}
|
||||
@@ -140,7 +135,7 @@ authorizeMPToken(
|
||||
// When a holder wants to unauthorize/delete a MPT, the ledger must
|
||||
// - delete mptokenKey from owner directory
|
||||
// - delete the MPToken
|
||||
if ((flags & tfMPTUnauthorize) != 0u)
|
||||
if ((flags & tfMPTUnauthorize) != 0)
|
||||
{
|
||||
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
|
||||
auto const sleMpt = view.peek(mptokenKey);
|
||||
@@ -222,7 +217,7 @@ authorizeMPToken(
|
||||
|
||||
// Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on
|
||||
// their MPToken
|
||||
if ((flags & tfMPTUnauthorize) != 0u)
|
||||
if ((flags & tfMPTUnauthorize) != 0)
|
||||
{
|
||||
flagsOut &= ~lsfMPTAuthorized;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,11 @@ Permission::Permission()
|
||||
#pragma pop_macro("PERMISSION")
|
||||
};
|
||||
|
||||
XRPL_ASSERT(
|
||||
txFeatureMap_.size() == delegableTx_.size(),
|
||||
"xrpl::Permission : txFeatureMap_ and delegableTx_ must have same "
|
||||
"size");
|
||||
|
||||
for ([[maybe_unused]] auto const& permission : granularPermissionMap_)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
|
||||
@@ -215,6 +215,7 @@ transResults()
|
||||
MAKE_ERROR(terNO_AMM, "AMM doesn't exist for the asset pair."),
|
||||
MAKE_ERROR(terADDRESS_COLLISION, "Failed to allocate an unique account address."),
|
||||
MAKE_ERROR(terNO_DELEGATE_PERMISSION, "Delegated account lacks permission to perform this transaction."),
|
||||
MAKE_ERROR(terLOCKED, "Fund is locked."),
|
||||
|
||||
MAKE_ERROR(tesSUCCESS, "The transaction was applied. Only final in a validated ledger."),
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ NotTEC
|
||||
checkTxPermission(std::shared_ptr<SLE const> const& delegate, STTx const& tx)
|
||||
{
|
||||
if (!delegate)
|
||||
return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE
|
||||
return terNO_DELEGATE_PERMISSION;
|
||||
|
||||
auto const permissionArray = delegate->getFieldArray(sfPermissions);
|
||||
auto const txPermission = tx.getTxnType() + 1;
|
||||
@@ -28,7 +28,7 @@ loadGranularPermission(
|
||||
std::unordered_set<GranularPermissionType>& granularPermissions)
|
||||
{
|
||||
if (!delegate)
|
||||
return; // LCOV_EXCL_LINE
|
||||
return;
|
||||
|
||||
auto const permissionArray = delegate->getFieldArray(sfPermissions);
|
||||
for (auto const& permission : permissionArray)
|
||||
|
||||
@@ -594,6 +594,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
return {tecEXPIRED, true};
|
||||
}
|
||||
|
||||
bool const bOpenLedger = sb.open();
|
||||
bool crossed = false;
|
||||
|
||||
if (isTesSuccess(result))
|
||||
@@ -682,7 +683,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
stream << " out: " << format_amount(place_offer.out);
|
||||
}
|
||||
|
||||
if (result == tecFAILED_PROCESSING && sb.open())
|
||||
if (result == tecFAILED_PROCESSING && bOpenLedger)
|
||||
result = telFAILED_PROCESSING;
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
|
||||
@@ -265,6 +265,7 @@ Payment::checkPermission(ReadView const& view, STTx const& tx)
|
||||
tx.isFieldPresent(sfPaths))
|
||||
return terNO_DELEGATE_PERMISSION;
|
||||
|
||||
// PaymentMint and PaymentBurn apply to both IOU and MPT direct payments.
|
||||
if (granularPermissions.contains(PaymentMint) && !isXRP(amountAsset) &&
|
||||
amountAsset.getIssuer() == tx[sfAccount])
|
||||
return tesSUCCESS;
|
||||
@@ -401,7 +402,6 @@ Payment::doApply()
|
||||
sleDst = std::make_shared<SLE>(k);
|
||||
sleDst->setAccountID(sfAccount, dstAccountID);
|
||||
sleDst->setFieldU32(sfSequence, view().seq());
|
||||
sleDst->setFieldAmount(sfBalance, XRPAmount(beast::zero));
|
||||
|
||||
view().insert(sleDst);
|
||||
}
|
||||
|
||||
@@ -4155,8 +4155,12 @@ class Batch_test : public beast::unit_test::suite
|
||||
std::vector<TestLedgerData> const testCases = {
|
||||
{0, "Batch", "tesSUCCESS", batchID, std::nullopt},
|
||||
{1, "TrustSet", "tesSUCCESS", txIDs[0], batchID},
|
||||
// jv2 fails with terNO_DELEGATE_PERMISSION.
|
||||
};
|
||||
validateClosedLedger(env, testCases);
|
||||
|
||||
// verify jv2 is not present in the closed ledger.
|
||||
BEAST_EXPECT(env.rpc("tx", txIDs[1])[jss::result][jss::error] == "txnNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <test/jtx/CaptureLogs.h>
|
||||
#include <test/jtx/delegate.h>
|
||||
|
||||
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Permissions.h>
|
||||
|
||||
@@ -1856,6 +1857,21 @@ class Delegate_test : public beast::unit_test::suite
|
||||
"\n Action: Verify security requirements to interact with Delegation feature");
|
||||
}
|
||||
|
||||
void
|
||||
testDelegateUtilsNullptrCheck()
|
||||
{
|
||||
testcase("DelegateUtils nullptr check");
|
||||
|
||||
// checkTxPermission nullptr check
|
||||
STTx const tx{ttPAYMENT, [](STObject&) {}};
|
||||
BEAST_EXPECT(checkTxPermission(nullptr, tx) == terNO_DELEGATE_PERMISSION);
|
||||
|
||||
// loadGranularPermission nullptr check
|
||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
||||
loadGranularPermission(nullptr, ttPAYMENT, granularPermissions);
|
||||
BEAST_EXPECT(granularPermissions.empty());
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -1881,6 +1897,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
testPermissionValue(all);
|
||||
testTxRequireFeatures(all);
|
||||
testTxDelegableCount();
|
||||
testDelegateUtilsNullptrCheck();
|
||||
}
|
||||
};
|
||||
BEAST_DEFINE_TESTSUITE(Delegate, app, xrpl);
|
||||
|
||||
@@ -29,7 +29,7 @@ static LimitRange constexpr accountOffers = {10, 200, 400};
|
||||
static LimitRange constexpr accountTx = {10, 200, 400};
|
||||
|
||||
/** Limits for the book_offers command. */
|
||||
static LimitRange constexpr bookOffers = {0, 60, 100};
|
||||
static LimitRange constexpr bookOffers = {1, 60, 100};
|
||||
|
||||
/** Limits for the no_ripple_check command. */
|
||||
static LimitRange constexpr noRippleCheck = {10, 300, 400};
|
||||
|
||||
Reference in New Issue
Block a user