mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-04 20:02:29 +00:00
Compare commits
6 Commits
ximinez/le
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c69091bded | ||
|
|
595f0dd461 | ||
|
|
b451d5e412 | ||
|
|
af97df5a63 | ||
|
|
e39954d128 | ||
|
|
3cd1e3d94e |
@@ -24,6 +24,7 @@ Checks: "-*,
|
||||
bugprone-misplaced-operator-in-strlen-in-alloc,
|
||||
bugprone-misplaced-pointer-arithmetic-in-alloc,
|
||||
bugprone-misplaced-widening-cast,
|
||||
bugprone-move-forwarding-reference,
|
||||
bugprone-multi-level-implicit-pointer-conversion,
|
||||
bugprone-multiple-new-in-one-expression,
|
||||
bugprone-multiple-statement-macro,
|
||||
@@ -32,10 +33,12 @@ Checks: "-*,
|
||||
bugprone-parent-virtual-call,
|
||||
bugprone-posix-return,
|
||||
bugprone-redundant-branch-condition,
|
||||
bugprone-return-const-ref-from-parameter,
|
||||
bugprone-shared-ptr-array-mismatch,
|
||||
bugprone-signal-handler,
|
||||
bugprone-signed-char-misuse,
|
||||
bugprone-sizeof-container,
|
||||
bugprone-sizeof-expression,
|
||||
bugprone-spuriously-wake-up-functions,
|
||||
bugprone-standalone-empty,
|
||||
bugprone-string-constructor,
|
||||
@@ -82,16 +85,14 @@ Checks: "-*,
|
||||
performance-trivially-destructible
|
||||
"
|
||||
# ---
|
||||
# checks that have some issues that need to be resolved:
|
||||
# more checks that have some issues that need to be resolved:
|
||||
#
|
||||
# bugprone-crtp-constructor-accessibility,
|
||||
# bugprone-inc-dec-in-conditions,
|
||||
# bugprone-reserved-identifier,
|
||||
# bugprone-move-forwarding-reference,
|
||||
# bugprone-unused-local-non-trivial-variable,
|
||||
# bugprone-return-const-ref-from-parameter,
|
||||
# bugprone-switch-missing-default-case,
|
||||
# bugprone-sizeof-expression,
|
||||
# bugprone-suspicious-stringview-data-usage,
|
||||
# bugprone-suspicious-missing-comma,
|
||||
# bugprone-pointer-arithmetic-on-polymorphic-object,
|
||||
|
||||
5
.github/workflows/publish-docs.yml
vendored
5
.github/workflows/publish-docs.yml
vendored
@@ -49,6 +49,11 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@2cbf481018d930656e9276fcc20dc0e3a0be5b6d
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
- name: Get number of processors
|
||||
uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf
|
||||
id: nproc
|
||||
|
||||
@@ -36,26 +36,6 @@ endif ()
|
||||
# Enable ccache to speed up builds.
|
||||
include(Ccache)
|
||||
|
||||
# make GIT_COMMIT_HASH define available to all sources
|
||||
find_package(Git)
|
||||
if (Git_FOUND)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse
|
||||
HEAD OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
|
||||
if (gch)
|
||||
set(GIT_COMMIT_HASH "${gch}")
|
||||
message(STATUS gch: ${GIT_COMMIT_HASH})
|
||||
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
||||
endif ()
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse
|
||||
--abbrev-ref HEAD OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gb)
|
||||
if (gb)
|
||||
set(GIT_BRANCH "${gb}")
|
||||
message(STATUS gb: ${GIT_BRANCH})
|
||||
add_definitions(-DGIT_BRANCH="${GIT_BRANCH}")
|
||||
endif ()
|
||||
endif () # git
|
||||
|
||||
if (thread_safety_analysis)
|
||||
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS
|
||||
-DXRPL_ENABLE_THREAD_SAFETY_ANNOTATIONS)
|
||||
|
||||
21
cmake/GitInfo.cmake
Normal file
21
cmake/GitInfo.cmake
Normal file
@@ -0,0 +1,21 @@
|
||||
include_guard()
|
||||
|
||||
set(GIT_BUILD_BRANCH "")
|
||||
set(GIT_COMMIT_HASH "")
|
||||
|
||||
find_package(Git)
|
||||
if (NOT Git_FOUND)
|
||||
message(WARNING "Git not found. Git branch and commit hash will be empty.")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
set(GIT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.git)
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse --abbrev-ref HEAD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE GIT_BUILD_BRANCH)
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse HEAD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE GIT_COMMIT_HASH)
|
||||
|
||||
message(STATUS "Git branch: ${GIT_BUILD_BRANCH}")
|
||||
message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}")
|
||||
@@ -58,6 +58,12 @@ include(target_link_modules)
|
||||
add_module(xrpl beast)
|
||||
target_link_libraries(xrpl.libxrpl.beast PUBLIC xrpl.imports.main)
|
||||
|
||||
include(GitInfo)
|
||||
add_module(xrpl git)
|
||||
target_compile_definitions(xrpl.libxrpl.git PRIVATE GIT_COMMIT_HASH="${GIT_COMMIT_HASH}"
|
||||
GIT_BUILD_BRANCH="${GIT_BUILD_BRANCH}")
|
||||
target_link_libraries(xrpl.libxrpl.git PUBLIC xrpl.imports.main)
|
||||
|
||||
# Level 02
|
||||
add_module(xrpl basics)
|
||||
target_link_libraries(xrpl.libxrpl.basics PUBLIC xrpl.libxrpl.beast)
|
||||
@@ -71,7 +77,8 @@ target_link_libraries(xrpl.libxrpl.crypto PUBLIC xrpl.libxrpl.basics)
|
||||
|
||||
# Level 04
|
||||
add_module(xrpl protocol)
|
||||
target_link_libraries(xrpl.libxrpl.protocol PUBLIC xrpl.libxrpl.crypto xrpl.libxrpl.json)
|
||||
target_link_libraries(xrpl.libxrpl.protocol PUBLIC xrpl.libxrpl.crypto xrpl.libxrpl.git
|
||||
xrpl.libxrpl.json)
|
||||
|
||||
# Level 05
|
||||
add_module(xrpl core)
|
||||
@@ -135,6 +142,7 @@ target_link_modules(
|
||||
conditions
|
||||
core
|
||||
crypto
|
||||
git
|
||||
json
|
||||
ledger
|
||||
net
|
||||
|
||||
@@ -23,6 +23,7 @@ install(TARGETS common
|
||||
xrpl.libxrpl.conditions
|
||||
xrpl.libxrpl.core
|
||||
xrpl.libxrpl.crypto
|
||||
xrpl.libxrpl.git
|
||||
xrpl.libxrpl.json
|
||||
xrpl.libxrpl.rdb
|
||||
xrpl.libxrpl.ledger
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace beast {
|
||||
@@ -26,14 +27,14 @@ public:
|
||||
|
||||
SemanticVersion();
|
||||
|
||||
SemanticVersion(std::string const& version);
|
||||
SemanticVersion(std::string_view version);
|
||||
|
||||
/** Parse a semantic version string.
|
||||
The parsing is as strict as possible.
|
||||
@return `true` if the string was parsed.
|
||||
*/
|
||||
bool
|
||||
parse(std::string const& input);
|
||||
parse(std::string_view input);
|
||||
|
||||
/** Produce a string from semantic version components. */
|
||||
std::string
|
||||
|
||||
13
include/xrpl/git/Git.h
Normal file
13
include/xrpl/git/Git.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::git {
|
||||
|
||||
std::string const&
|
||||
getCommitHash();
|
||||
|
||||
std::string const&
|
||||
getBuildBranch();
|
||||
|
||||
} // namespace xrpl::git
|
||||
@@ -49,7 +49,7 @@ getFullVersionString();
|
||||
@return the encoded version in a 64-bit integer
|
||||
*/
|
||||
std::uint64_t
|
||||
encodeSoftwareVersion(char const* const versionStr);
|
||||
encodeSoftwareVersion(std::string_view versionStr);
|
||||
|
||||
/** Returns this server's version packed in a 64-bit integer. */
|
||||
std::uint64_t
|
||||
|
||||
@@ -138,14 +138,14 @@ SemanticVersion::SemanticVersion() : majorVersion(0), minorVersion(0), patchVers
|
||||
{
|
||||
}
|
||||
|
||||
SemanticVersion::SemanticVersion(std::string const& version) : SemanticVersion()
|
||||
SemanticVersion::SemanticVersion(std::string_view version) : SemanticVersion()
|
||||
{
|
||||
if (!parse(version))
|
||||
throw std::invalid_argument("invalid version string");
|
||||
}
|
||||
|
||||
bool
|
||||
SemanticVersion::parse(std::string const& input)
|
||||
SemanticVersion::parse(std::string_view input)
|
||||
{
|
||||
// May not have leading or trailing whitespace
|
||||
auto left_iter = std::find_if_not(input.begin(), input.end(), [](std::string::value_type c) {
|
||||
|
||||
31
src/libxrpl/git/Git.cpp
Normal file
31
src/libxrpl/git/Git.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "xrpl/git/Git.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef GIT_COMMIT_HASH
|
||||
#error "GIT_COMMIT_HASH must be defined"
|
||||
#endif
|
||||
#ifndef GIT_BUILD_BRANCH
|
||||
#error "GIT_BUILD_BRANCH must be defined"
|
||||
#endif
|
||||
|
||||
namespace xrpl::git {
|
||||
|
||||
static constexpr char kGIT_COMMIT_HASH[] = GIT_COMMIT_HASH;
|
||||
static constexpr char kGIT_BUILD_BRANCH[] = GIT_BUILD_BRANCH;
|
||||
|
||||
std::string const&
|
||||
getCommitHash()
|
||||
{
|
||||
static std::string const kVALUE = kGIT_COMMIT_HASH;
|
||||
return kVALUE;
|
||||
}
|
||||
|
||||
std::string const&
|
||||
getBuildBranch()
|
||||
{
|
||||
static std::string const kVALUE = kGIT_BUILD_BRANCH;
|
||||
return kVALUE;
|
||||
}
|
||||
|
||||
} // namespace xrpl::git
|
||||
@@ -2020,99 +2020,6 @@ rippleSendIOU(
|
||||
return terResult;
|
||||
}
|
||||
|
||||
template <class TAsset>
|
||||
static TER
|
||||
doSendMulti(
|
||||
std::string const& name,
|
||||
ApplyView& view,
|
||||
AccountID const& senderID,
|
||||
TAsset const& issue,
|
||||
MultiplePaymentDestinations const& receivers,
|
||||
STAmount& actual,
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee,
|
||||
// Don't pass back parameters that the caller already has
|
||||
std::function<
|
||||
TER(AccountID const& senderID,
|
||||
AccountID const& receiverID,
|
||||
STAmount const& amount,
|
||||
bool checkIssuer)> doCredit,
|
||||
std::function<
|
||||
TER(AccountID const& issuer, STAmount const& takeFromSender, STAmount const& amount)>
|
||||
preMint = {})
|
||||
{
|
||||
// Use the same pattern for all the SendMulti functions to help avoid
|
||||
// divergence and copy/paste errors.
|
||||
auto const& issuer = issue.getIssuer();
|
||||
|
||||
// These values may not stay in sync
|
||||
STAmount takeFromSender{issue};
|
||||
actual = takeFromSender;
|
||||
|
||||
// Failures return immediately.
|
||||
for (auto const& r : receivers)
|
||||
{
|
||||
auto const& receiverID = r.first;
|
||||
STAmount amount{issue, r.second};
|
||||
|
||||
if (amount < beast::zero)
|
||||
{
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
/* If we aren't sending anything or if the sender is the same as the
|
||||
* receiver then we don't need to do anything.
|
||||
*/
|
||||
if (!amount || (senderID == receiverID))
|
||||
continue;
|
||||
|
||||
using namespace std::string_literals;
|
||||
XRPL_ASSERT(!isXRP(receiverID), ("xrpl::"s + name + " : receiver is not XRP").c_str());
|
||||
|
||||
if (senderID == issuer || receiverID == issuer || issuer == noAccount())
|
||||
{
|
||||
if (preMint)
|
||||
{
|
||||
if (auto const ter = preMint(issuer, takeFromSender, amount))
|
||||
return ter;
|
||||
}
|
||||
// Direct send: redeeming IOUs and/or sending own IOUs.
|
||||
if (auto const ter = doCredit(senderID, receiverID, amount, false))
|
||||
return ter;
|
||||
actual += amount;
|
||||
// Do not add amount to takeFromSender, because doCredit took
|
||||
// it.
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sending 3rd party: transit.
|
||||
|
||||
// Calculate the amount to transfer accounting
|
||||
// for any transfer fees if the fee is not waived:
|
||||
STAmount actualSend = (waiveFee == WaiveTransferFee::Yes || issue.native())
|
||||
? amount
|
||||
: multiply(amount, transferRate(view, amount));
|
||||
actual += actualSend;
|
||||
takeFromSender += actualSend;
|
||||
|
||||
JLOG(j.debug()) << name << "> " << to_string(senderID) << " - > " << to_string(receiverID)
|
||||
<< " : deliver=" << amount.getFullText()
|
||||
<< " cost=" << actualSend.getFullText();
|
||||
|
||||
if (TER const terResult = doCredit(issuer, receiverID, amount, true))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
if (senderID != issuer && takeFromSender)
|
||||
{
|
||||
if (TER const terResult = doCredit(senderID, issuer, takeFromSender, true))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Send regardless of limits.
|
||||
// --> receivers: Amount/currency/issuer to deliver to receivers.
|
||||
// <-- saActual: Amount actually cost to sender. Sender pays fees.
|
||||
@@ -2126,18 +2033,65 @@ rippleSendMultiIOU(
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee)
|
||||
{
|
||||
auto const& issuer = issue.getIssuer();
|
||||
|
||||
XRPL_ASSERT(!isXRP(senderID), "xrpl::rippleSendMultiIOU : sender is not XRP");
|
||||
|
||||
auto doCredit = [&view, j](
|
||||
AccountID const& senderID,
|
||||
AccountID const& receiverID,
|
||||
STAmount const& amount,
|
||||
bool checkIssuer) {
|
||||
return rippleCreditIOU(view, senderID, receiverID, amount, checkIssuer, j);
|
||||
};
|
||||
// These may diverge
|
||||
STAmount takeFromSender{issue};
|
||||
actual = takeFromSender;
|
||||
|
||||
return doSendMulti(
|
||||
"rippleSendMultiIOU", view, senderID, issue, receivers, actual, j, waiveFee, doCredit);
|
||||
// Failures return immediately.
|
||||
for (auto const& r : receivers)
|
||||
{
|
||||
auto const& receiverID = r.first;
|
||||
STAmount amount{issue, r.second};
|
||||
|
||||
/* If we aren't sending anything or if the sender is the same as the
|
||||
* receiver then we don't need to do anything.
|
||||
*/
|
||||
if (!amount || (senderID == receiverID))
|
||||
continue;
|
||||
|
||||
XRPL_ASSERT(!isXRP(receiverID), "xrpl::rippleSendMultiIOU : receiver is not XRP");
|
||||
|
||||
if (senderID == issuer || receiverID == issuer || issuer == noAccount())
|
||||
{
|
||||
// Direct send: redeeming IOUs and/or sending own IOUs.
|
||||
if (auto const ter = rippleCreditIOU(view, senderID, receiverID, amount, false, j))
|
||||
return ter;
|
||||
actual += amount;
|
||||
// Do not add amount to takeFromSender, because rippleCreditIOU took
|
||||
// it.
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sending 3rd party IOUs: transit.
|
||||
|
||||
// Calculate the amount to transfer accounting
|
||||
// for any transfer fees if the fee is not waived:
|
||||
STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
|
||||
? amount
|
||||
: multiply(amount, transferRate(view, issuer));
|
||||
actual += actualSend;
|
||||
takeFromSender += actualSend;
|
||||
|
||||
JLOG(j.debug()) << "rippleSendMultiIOU> " << to_string(senderID) << " - > "
|
||||
<< to_string(receiverID) << " : deliver=" << amount.getFullText()
|
||||
<< " cost=" << actual.getFullText();
|
||||
|
||||
if (TER const terResult = rippleCreditIOU(view, issuer, receiverID, amount, true, j))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
if (senderID != issuer && takeFromSender)
|
||||
{
|
||||
if (TER const terResult = rippleCreditIOU(view, senderID, issuer, takeFromSender, true, j))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
static TER
|
||||
@@ -2271,9 +2225,9 @@ accountSendMultiIOU(
|
||||
XRPL_ASSERT_PARTS(
|
||||
receivers.size() > 1, "xrpl::accountSendMultiIOU", "multiple recipients provided");
|
||||
|
||||
STAmount actual;
|
||||
if (!issue.native())
|
||||
{
|
||||
STAmount actual;
|
||||
JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID) << " sending "
|
||||
<< receivers.size() << " IOUs";
|
||||
|
||||
@@ -2300,85 +2254,6 @@ accountSendMultiIOU(
|
||||
<< receivers.size() << " receivers.";
|
||||
}
|
||||
|
||||
auto doCredit = [&view, &sender, &receivers, j](
|
||||
AccountID const& senderID,
|
||||
AccountID const& receiverID,
|
||||
STAmount const& amount,
|
||||
bool /*checkIssuer*/) -> TER {
|
||||
if (!senderID)
|
||||
{
|
||||
SLE::pointer receiver =
|
||||
receiverID != beast::zero ? view.peek(keylet::account(receiverID)) : SLE::pointer();
|
||||
|
||||
if (auto stream = j.trace())
|
||||
{
|
||||
std::string receiver_bal("-");
|
||||
|
||||
if (receiver)
|
||||
receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
|
||||
|
||||
stream << "accountSendMultiIOU> " << to_string(senderID) << " -> "
|
||||
<< to_string(receiverID) << " (" << receiver_bal
|
||||
<< ") : " << amount.getFullText();
|
||||
}
|
||||
|
||||
if (receiver)
|
||||
{
|
||||
// Increment XRP balance.
|
||||
auto const rcvBal = receiver->getFieldAmount(sfBalance);
|
||||
receiver->setFieldAmount(sfBalance, rcvBal + amount);
|
||||
view.creditHook(xrpAccount(), receiverID, amount, -rcvBal);
|
||||
|
||||
view.update(receiver);
|
||||
}
|
||||
|
||||
if (auto stream = j.trace())
|
||||
{
|
||||
std::string receiver_bal("-");
|
||||
|
||||
if (receiver)
|
||||
receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
|
||||
|
||||
stream << "accountSendMultiIOU< " << to_string(senderID) << " -> "
|
||||
<< to_string(receiverID) << " (" << receiver_bal
|
||||
<< ") : " << amount.getFullText();
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
// Sender
|
||||
if (sender)
|
||||
{
|
||||
if (sender->getFieldAmount(sfBalance) < amount)
|
||||
{
|
||||
return TER{tecFAILED_PROCESSING};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const sndBal = sender->getFieldAmount(sfBalance);
|
||||
view.creditHook(senderID, xrpAccount(), amount, sndBal);
|
||||
|
||||
// Decrement XRP balance.
|
||||
sender->setFieldAmount(sfBalance, sndBal - amount);
|
||||
view.update(sender);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto stream = j.trace())
|
||||
{
|
||||
std::string sender_bal("-");
|
||||
|
||||
if (sender)
|
||||
sender_bal = sender->getFieldAmount(sfBalance).getFullText();
|
||||
|
||||
stream << "accountSendMultiIOU< " << to_string(senderID) << " (" << sender_bal
|
||||
<< ") -> " << receivers.size() << " receivers.";
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
};
|
||||
return doSendMulti(
|
||||
"accountSendMultiIOU", view, senderID, issue, receivers, actual, j, waiveFee, doCredit);
|
||||
|
||||
// Failures return immediately.
|
||||
STAmount takeFromSender{issue};
|
||||
for (auto const& r : receivers)
|
||||
@@ -2597,47 +2472,82 @@ rippleSendMultiMPT(
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee)
|
||||
{
|
||||
// Safe to get MPT since rippleSendMultiMPT is only called by
|
||||
// accountSendMultiMPT
|
||||
auto const& issuer = mptIssue.getIssuer();
|
||||
|
||||
auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()));
|
||||
if (!sle)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
auto preMint = [&](AccountID const& issuer,
|
||||
STAmount const& takeFromSender,
|
||||
STAmount const& amount) -> TER {
|
||||
// if sender is issuer, check that the new OutstandingAmount will
|
||||
// not exceed MaximumAmount
|
||||
if (senderID == issuer)
|
||||
// These may diverge
|
||||
STAmount takeFromSender{mptIssue};
|
||||
actual = takeFromSender;
|
||||
|
||||
for (auto const& r : receivers)
|
||||
{
|
||||
auto const& receiverID = r.first;
|
||||
STAmount amount{mptIssue, r.second};
|
||||
|
||||
if (amount < beast::zero)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
takeFromSender == beast::zero,
|
||||
"rippler::rippleSendMultiMPT",
|
||||
"sender == issuer, takeFromSender == zero");
|
||||
auto const sendAmount = amount.mpt().value();
|
||||
auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||
if (sendAmount > maximumAmount ||
|
||||
sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
|
||||
return tecPATH_DRY;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
};
|
||||
auto doCredit =
|
||||
[&view, j](
|
||||
AccountID const& senderID, AccountID const& receiverID, STAmount const& amount, bool) {
|
||||
return rippleCreditMPT(view, senderID, receiverID, amount, j);
|
||||
};
|
||||
/* If we aren't sending anything or if the sender is the same as the
|
||||
* receiver then we don't need to do anything.
|
||||
*/
|
||||
if (!amount || (senderID == receiverID))
|
||||
continue;
|
||||
|
||||
return doSendMulti(
|
||||
"rippleSendMultiMPT",
|
||||
view,
|
||||
senderID,
|
||||
mptIssue,
|
||||
receivers,
|
||||
actual,
|
||||
j,
|
||||
waiveFee,
|
||||
doCredit,
|
||||
preMint);
|
||||
if (senderID == issuer || receiverID == issuer)
|
||||
{
|
||||
// if sender is issuer, check that the new OutstandingAmount will
|
||||
// not exceed MaximumAmount
|
||||
if (senderID == issuer)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
takeFromSender == beast::zero,
|
||||
"rippler::rippleSendMultiMPT",
|
||||
"sender == issuer, takeFromSender == zero");
|
||||
auto const sendAmount = amount.mpt().value();
|
||||
auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||
if (sendAmount > maximumAmount ||
|
||||
sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
|
||||
// Direct send: redeeming MPTs and/or sending own MPTs.
|
||||
if (auto const ter = rippleCreditMPT(view, senderID, receiverID, amount, j))
|
||||
return ter;
|
||||
actual += amount;
|
||||
// Do not add amount to takeFromSender, because rippleCreditMPT took
|
||||
// it
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sending 3rd party MPTs: transit.
|
||||
STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
|
||||
? amount
|
||||
: multiply(amount, transferRate(view, amount.get<MPTIssue>().getMptID()));
|
||||
actual += actualSend;
|
||||
takeFromSender += actualSend;
|
||||
|
||||
JLOG(j.debug()) << "rippleSendMultiMPT> " << to_string(senderID) << " - > "
|
||||
<< to_string(receiverID) << " : deliver=" << amount.getFullText()
|
||||
<< " cost=" << actualSend.getFullText();
|
||||
|
||||
if (auto const terResult = rippleCreditMPT(view, issuer, receiverID, amount, j))
|
||||
return terResult;
|
||||
}
|
||||
if (senderID != issuer && takeFromSender)
|
||||
{
|
||||
if (TER const terResult = rippleCreditMPT(view, senderID, issuer, takeFromSender, j))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
static TER
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
#include <xrpl/beast/core/SemanticVersion.h>
|
||||
#include <xrpl/git/Git.h>
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
@@ -14,44 +15,60 @@ namespace xrpl {
|
||||
|
||||
namespace BuildInfo {
|
||||
|
||||
namespace {
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// The build version number. You must edit this for each release
|
||||
// and follow the format described at http://semver.org/
|
||||
//------------------------------------------------------------------------------
|
||||
// clang-format off
|
||||
char const* const versionString = "3.2.0-b0"
|
||||
// clang-format on
|
||||
|
||||
#if defined(DEBUG) || defined(SANITIZERS)
|
||||
"+"
|
||||
#ifdef GIT_COMMIT_HASH
|
||||
GIT_COMMIT_HASH
|
||||
"."
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
"DEBUG"
|
||||
#ifdef SANITIZERS
|
||||
"."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SANITIZERS
|
||||
BOOST_PP_STRINGIZE(SANITIZERS) // cspell: disable-line
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// clang-format on
|
||||
;
|
||||
|
||||
//
|
||||
// Don't touch anything below this line
|
||||
//
|
||||
|
||||
std::string
|
||||
buildVersionString()
|
||||
{
|
||||
std::string version = versionString;
|
||||
|
||||
#if defined(DEBUG) || defined(SANITIZERS)
|
||||
std::string metadata;
|
||||
|
||||
std::string const& commitHash = xrpl::git::getCommitHash();
|
||||
if (!commitHash.empty())
|
||||
metadata += commitHash + ".";
|
||||
|
||||
#ifdef DEBUG
|
||||
metadata += "DEBUG";
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG) && defined(SANITIZERS)
|
||||
metadata += ".";
|
||||
#endif
|
||||
|
||||
#ifdef SANITIZERS
|
||||
metadata += BOOST_PP_STRINGIZE(SANITIZERS); // cspell: disable-line
|
||||
#endif
|
||||
|
||||
if (!metadata.empty())
|
||||
version += "+" + metadata;
|
||||
#endif
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string const&
|
||||
getVersionString()
|
||||
{
|
||||
static std::string const value = [] {
|
||||
std::string const s = versionString;
|
||||
std::string const s = buildVersionString();
|
||||
|
||||
beast::SemanticVersion v;
|
||||
if (!v.parse(s) || v.print() != s)
|
||||
LogicError(s + ": Bad server version string");
|
||||
@@ -71,13 +88,13 @@ static constexpr std::uint64_t implementationVersionIdentifier = 0x183B'0000'000
|
||||
static constexpr std::uint64_t implementationVersionIdentifierMask = 0xFFFF'0000'0000'0000LLU;
|
||||
|
||||
std::uint64_t
|
||||
encodeSoftwareVersion(char const* const versionStr)
|
||||
encodeSoftwareVersion(std::string_view versionStr)
|
||||
{
|
||||
std::uint64_t c = implementationVersionIdentifier;
|
||||
|
||||
beast::SemanticVersion v;
|
||||
|
||||
if (v.parse(std::string(versionStr)))
|
||||
if (v.parse(versionStr))
|
||||
{
|
||||
if (v.majorVersion >= 0 && v.majorVersion <= 255)
|
||||
c |= static_cast<std::uint64_t>(v.majorVersion) << 40;
|
||||
@@ -137,7 +154,7 @@ encodeSoftwareVersion(char const* const versionStr)
|
||||
std::uint64_t
|
||||
getEncodedVersion()
|
||||
{
|
||||
static std::uint64_t const cookie = {encodeSoftwareVersion(versionString)};
|
||||
static std::uint64_t const cookie = {encodeSoftwareVersion(getVersionString())};
|
||||
return cookie;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ TER
|
||||
PermissionedDomainDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const domain = ctx.tx.getFieldH256(sfDomainID);
|
||||
auto const sleDomain = ctx.view.read({ltPERMISSIONED_DOMAIN, domain});
|
||||
auto const sleDomain = ctx.view.read(keylet::permissionedDomain(domain));
|
||||
|
||||
if (!sleDomain)
|
||||
return tecNO_ENTRY;
|
||||
@@ -40,7 +40,7 @@ PermissionedDomainDelete::doApply()
|
||||
ctx_.tx.isFieldPresent(sfDomainID),
|
||||
"xrpl::PermissionedDomainDelete::doApply : required field present");
|
||||
|
||||
auto const slePd = view().peek({ltPERMISSIONED_DOMAIN, ctx_.tx.at(sfDomainID)});
|
||||
auto const slePd = view().peek(keylet::permissionedDomain(ctx_.tx.at(sfDomainID)));
|
||||
auto const page = (*slePd)[sfOwnerNode];
|
||||
|
||||
if (!view().dirRemove(keylet::ownerDir(account_), page, slePd->key(), true))
|
||||
|
||||
@@ -227,7 +227,7 @@ public:
|
||||
static typename Base::Key const&
|
||||
extract(Value const& value)
|
||||
{
|
||||
return value;
|
||||
return value; // NOLINT(bugprone-return-const-ref-from-parameter)
|
||||
}
|
||||
|
||||
static Values
|
||||
|
||||
@@ -556,7 +556,7 @@ struct MultiApiJson_test : beast::unit_test::suite
|
||||
static_assert([](auto&& v) {
|
||||
return !requires {
|
||||
v.visitor(
|
||||
std::move(v), // cannot bind rvalue
|
||||
decltype(v){}, // cannot bind rvalue
|
||||
1,
|
||||
[](Json::Value&, auto) {});
|
||||
};
|
||||
|
||||
@@ -223,6 +223,45 @@ public:
|
||||
expect(jv[jss::result][jss::obligations]["USD"] == maxUSD.getText());
|
||||
}
|
||||
|
||||
void
|
||||
testGWBWithMPT()
|
||||
{
|
||||
testcase("Gateway Balances with MPT Escrow");
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
|
||||
// Ensure MPT is enabled
|
||||
FeatureBitset features = testable_amendments() | featureMPTokensV1;
|
||||
Env env(*this, features);
|
||||
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env.close();
|
||||
|
||||
// Create MPT issuance (Alice) with Escrow capability
|
||||
MPTTester mpt(env, alice, {.holders = {bob}, .fund = false});
|
||||
mpt.create({.flags = tfMPTCanEscrow});
|
||||
|
||||
// Authorize Bob and fund him
|
||||
mpt.authorize({.account = bob, .holderCount = 1});
|
||||
mpt.pay(alice, bob, 1000);
|
||||
|
||||
// Bob creates an escrow of MPT to Alice.
|
||||
auto const MPT = mpt["MPT"];
|
||||
env(escrow::create(bob, alice, MPT(100)), escrow::finish_time(env.now() + 10s));
|
||||
env.close();
|
||||
|
||||
// Query gateway_balances for Bob.
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value qry;
|
||||
qry[jss::account] = bob.human();
|
||||
|
||||
auto jv = wsc->invoke("gateway_balances", qry);
|
||||
expect(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -233,7 +272,7 @@ public:
|
||||
testGWB(feature);
|
||||
testGWBApiVersions(feature);
|
||||
}
|
||||
|
||||
testGWBWithMPT();
|
||||
testGWBOverflow();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/core/CurrentThreadName.h>
|
||||
#include <xrpl/git/Git.h>
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
#include <xrpl/server/Vacuum.h>
|
||||
|
||||
@@ -476,12 +477,8 @@ run(int argc, char** argv)
|
||||
if (vm.count("version"))
|
||||
{
|
||||
std::cout << "rippled version " << BuildInfo::getVersionString() << std::endl;
|
||||
#ifdef GIT_COMMIT_HASH
|
||||
std::cout << "Git commit hash: " << GIT_COMMIT_HASH << std::endl;
|
||||
#endif
|
||||
#ifdef GIT_BRANCH
|
||||
std::cout << "Git build branch: " << GIT_BRANCH << std::endl;
|
||||
#endif
|
||||
std::cout << "Git commit hash: " << xrpl::git::getCommitHash() << std::endl;
|
||||
std::cout << "Git build branch: " << xrpl::git::getBuildBranch() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <xrpl/core/PerfLog.h>
|
||||
#include <xrpl/crypto/RFC1751.h>
|
||||
#include <xrpl/crypto/csprng.h>
|
||||
#include <xrpl/git/Git.h>
|
||||
#include <xrpl/ledger/AmendmentTable.h>
|
||||
#include <xrpl/ledger/OrderBookDB.h>
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
@@ -2593,17 +2594,14 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(GIT_COMMIT_HASH) || defined(GIT_BRANCH)
|
||||
if (!xrpl::git::getCommitHash().empty() || !xrpl::git::getBuildBranch().empty())
|
||||
{
|
||||
auto& x = (info[jss::git] = Json::objectValue);
|
||||
#ifdef GIT_COMMIT_HASH
|
||||
x[jss::hash] = GIT_COMMIT_HASH;
|
||||
#endif
|
||||
#ifdef GIT_BRANCH
|
||||
x[jss::branch] = GIT_BRANCH;
|
||||
#endif
|
||||
if (!xrpl::git::getCommitHash().empty())
|
||||
x[jss::hash] = xrpl::git::getCommitHash();
|
||||
if (!xrpl::git::getBuildBranch().empty())
|
||||
x[jss::branch] = xrpl::git::getBuildBranch();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
info[jss::io_latency_ms] = static_cast<Json::UInt>(registry_.app().getIOLatency().count());
|
||||
|
||||
|
||||
@@ -131,6 +131,10 @@ doGatewayBalances(RPC::JsonContext& context)
|
||||
if (sle->getType() == ltESCROW)
|
||||
{
|
||||
auto const& escrow = sle->getFieldAmount(sfAmount);
|
||||
// Gateway Balance should not include MPTs
|
||||
if (escrow.holds<MPTIssue>())
|
||||
return;
|
||||
|
||||
auto& bal = locked[escrow.getCurrency()];
|
||||
if (bal == beast::zero)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user