mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-10 14:02:28 +00:00
Compare commits
29 Commits
3.2.0-b3
...
copilot/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40a5871c41 | ||
|
|
6c64a2cc3f | ||
|
|
c8fc57b76a | ||
|
|
c7627adeba | ||
|
|
25e6606056 | ||
|
|
02fa55df8d | ||
|
|
6e2452207d | ||
|
|
29e49abd3c | ||
|
|
ae21f53e4d | ||
|
|
bee1056faa | ||
|
|
b6aa4a8fde | ||
|
|
a9afd2c116 | ||
|
|
2502befb42 | ||
|
|
c3fae847f3 | ||
|
|
7f53351920 | ||
|
|
bb95a7d6cd | ||
|
|
5c8dfe5456 | ||
|
|
ab8c168e3b | ||
|
|
3a477e4d01 | ||
|
|
96bfc32fe2 | ||
|
|
de671863e2 | ||
|
|
e0cabb9f8c | ||
|
|
3d9c545f59 | ||
|
|
9b944ee8c2 | ||
|
|
509677abfd | ||
|
|
addc1e8e25 | ||
|
|
faf69da4b0 | ||
|
|
76e3b4fb0f | ||
|
|
e8bdbf975a |
@@ -10,6 +10,7 @@
|
||||
#include <cctype>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace beast {
|
||||
@@ -181,7 +182,7 @@ split_commas(FwdIt first, FwdIt last)
|
||||
|
||||
template <class Result = std::vector<std::string>>
|
||||
Result
|
||||
split_commas(boost::beast::string_view const& s)
|
||||
split_commas(std::string_view const& s)
|
||||
{
|
||||
return split_commas(s.begin(), s.end());
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace Json {
|
||||
|
||||
class Value;
|
||||
|
||||
using Output = std::function<void(boost::beast::string_view const&)>;
|
||||
using Output = std::function<void(std::string_view const&)>;
|
||||
|
||||
inline Output
|
||||
stringOutput(std::string& s)
|
||||
{
|
||||
return [&](boost::beast::string_view const& b) { s.append(b.data(), b.size()); };
|
||||
return [&](std::string_view const& b) { s.append(b.data(), b.size()); };
|
||||
}
|
||||
|
||||
/** Writes a minimal representation of a Json value to an Output in O(n) time.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -48,8 +49,7 @@ private:
|
||||
bool ping_active_ = false;
|
||||
boost::beast::websocket::ping_data payload_;
|
||||
error_code ec_;
|
||||
std::function<void(boost::beast::websocket::frame_type, boost::beast::string_view)>
|
||||
control_callback_;
|
||||
std::function<void(boost::beast::websocket::frame_type, std::string_view)> control_callback_;
|
||||
|
||||
public:
|
||||
template <class Body, class Headers>
|
||||
@@ -137,7 +137,7 @@ protected:
|
||||
on_ping(error_code const& ec);
|
||||
|
||||
void
|
||||
on_ping_pong(boost::beast::websocket::frame_type kind, boost::beast::string_view payload);
|
||||
on_ping_pong(boost::beast::websocket::frame_type kind, std::string_view payload);
|
||||
|
||||
void
|
||||
on_timer(error_code ec);
|
||||
@@ -414,11 +414,11 @@ template <class Handler, class Impl>
|
||||
void
|
||||
BaseWSPeer<Handler, Impl>::on_ping_pong(
|
||||
boost::beast::websocket::frame_type kind,
|
||||
boost::beast::string_view payload)
|
||||
std::string_view payload)
|
||||
{
|
||||
if (kind == boost::beast::websocket::frame_type::pong)
|
||||
{
|
||||
boost::beast::string_view const p(payload_.begin());
|
||||
std::string_view const p(payload_.begin(), payload_.size());
|
||||
if (payload == p)
|
||||
{
|
||||
close_on_timer_ = false;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -87,14 +88,14 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
output(boost::beast::string_view const& bytes)
|
||||
output(std::string_view const& bytes)
|
||||
{
|
||||
markStarted();
|
||||
output_(bytes);
|
||||
}
|
||||
|
||||
void
|
||||
stringOutput(boost::beast::string_view const& bytes)
|
||||
stringOutput(std::string_view const& bytes)
|
||||
{
|
||||
markStarted();
|
||||
std::size_t position = 0, writtenUntil = 0;
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace {
|
||||
// and follow the format described at http://semver.org/
|
||||
//------------------------------------------------------------------------------
|
||||
// clang-format off
|
||||
char const* const versionString = "3.2.0-b3"
|
||||
char const* const versionString = "3.2.0-b0"
|
||||
// clang-format on
|
||||
;
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
auto const assets = ctx.tx[sfAmount];
|
||||
auto const amount = ctx.tx[sfAmount];
|
||||
auto const vaultAsset = vault->at(sfAsset);
|
||||
auto const vaultShare = vault->at(sfShareMPTID);
|
||||
if (assets.asset() != vaultAsset && assets.asset() != vaultShare)
|
||||
if (amount.asset() != vaultAsset && amount.asset() != vaultShare)
|
||||
return tecWRONG_ASSET;
|
||||
|
||||
auto const& vaultAccount = vault->at(sfAccount);
|
||||
@@ -67,8 +67,53 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (auto const ret = canWithdraw(ctx.view, ctx.tx))
|
||||
return ret;
|
||||
if (ctx.view.rules().enabled(fixSecurity3_1_3) && amount.asset() == vaultShare)
|
||||
{
|
||||
// Post-fixSecurity3_1_3: if the user specified shares, convert
|
||||
// to the equivalent asset amount before checking withdrawal
|
||||
// limits. Pre-amendment the limit check was skipped for
|
||||
// share-denominated withdrawals.
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(vaultShare));
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(ctx.j.error()) << "VaultWithdraw: missing issuance of vault shares.";
|
||||
return tefINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto const maybeAssets = sharesToAssetsWithdraw(vault, sleIssuance, amount);
|
||||
if (!maybeAssets)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = canWithdraw(
|
||||
ctx.view,
|
||||
account,
|
||||
dstAcct,
|
||||
*maybeAssets,
|
||||
ctx.tx.isFieldPresent(sfDestinationTag)))
|
||||
return ret;
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
// It's easy to hit this exception from Number with large enough Scale
|
||||
// so we avoid spamming the log and only use debug here.
|
||||
JLOG(ctx.j.debug()) //
|
||||
<< "VaultWithdraw: overflow error with"
|
||||
<< " scale=" << (int)vault->at(sfScale) //
|
||||
<< ", assetsTotal=" << vault->at(sfAssetsTotal)
|
||||
<< ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount)
|
||||
<< ", amount=" << amount.value();
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto const ret = canWithdraw(ctx.view, ctx.tx))
|
||||
return ret;
|
||||
}
|
||||
|
||||
// If sending to Account (i.e. not a transfer), we will also create (only
|
||||
// if authorized) a trust line or MPToken as needed, in doApply().
|
||||
|
||||
@@ -5231,6 +5231,102 @@ class Vault_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
// Reproduction: canWithdraw IOU limit check bypassed when
|
||||
// withdrawal amount is specified in shares (MPT) rather than in assets.
|
||||
void
|
||||
testBug6_LimitBypassWithShares()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
testcase("Bug6 - limit bypass with share-denominated withdrawal");
|
||||
|
||||
auto const allAmendments = testable_amendments() | featureSingleAssetVault;
|
||||
|
||||
for (auto const& features : {allAmendments, allAmendments - fixSecurity3_1_3})
|
||||
{
|
||||
bool const withFix = features[fixSecurity3_1_3];
|
||||
|
||||
Env env{*this, features};
|
||||
Account const owner{"owner"};
|
||||
Account const issuer{"issuer"};
|
||||
Account const depositor{"depositor"};
|
||||
Account const charlie{"charlie"};
|
||||
Vault const vault{env};
|
||||
|
||||
env.fund(XRP(1000), issuer, owner, depositor, charlie);
|
||||
env(fset(issuer, asfAllowTrustLineClawback));
|
||||
env.close();
|
||||
|
||||
PrettyAsset const asset = issuer["IOU"];
|
||||
env.trust(asset(1000), owner);
|
||||
env.trust(asset(1000), depositor);
|
||||
env(pay(issuer, owner, asset(200)));
|
||||
env(pay(issuer, depositor, asset(200)));
|
||||
env.close();
|
||||
|
||||
// Charlie gets a LOW trustline limit of 5
|
||||
env.trust(asset(5), charlie);
|
||||
env.close();
|
||||
|
||||
auto const [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
auto const depositTx =
|
||||
vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
|
||||
env(depositTx);
|
||||
env.close();
|
||||
|
||||
// Get the share MPT info
|
||||
auto const vaultSle = env.le(keylet);
|
||||
if (!BEAST_EXPECT(vaultSle))
|
||||
return;
|
||||
auto const mptIssuanceID = vaultSle->at(sfShareMPTID);
|
||||
MPTIssue const shares(mptIssuanceID);
|
||||
PrettyAsset const share(shares);
|
||||
|
||||
// CONTROL: Withdraw 10 IOU (asset-denominated) to charlie.
|
||||
// Charlie's limit is 5, so this should be rejected with tecNO_LINE
|
||||
// regardless of the amendment.
|
||||
{
|
||||
auto withdrawTx =
|
||||
vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)});
|
||||
withdrawTx[sfDestination] = charlie.human();
|
||||
env(withdrawTx, ter{tecNO_LINE});
|
||||
env.close();
|
||||
}
|
||||
auto const charlieBalanceBefore = env.balance(charlie, asset.raw().get<Issue>());
|
||||
|
||||
// Withdraw the equivalent amount in shares to charlie.
|
||||
// Post-fix: rejected (tecNO_LINE) because the share amount is
|
||||
// converted to assets and the trustline limit is checked.
|
||||
// Pre-fix: succeeds (tesSUCCESS) because the limit check was
|
||||
// skipped for share-denominated withdrawals.
|
||||
{
|
||||
auto withdrawTx = vault.withdraw(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = STAmount(share, 10'000'000)});
|
||||
withdrawTx[sfDestination] = charlie.human();
|
||||
env(withdrawTx, ter{withFix ? TER{tecNO_LINE} : TER{tesSUCCESS}});
|
||||
env.close();
|
||||
|
||||
auto const charlieBalanceAfter = env.balance(charlie, asset.raw().get<Issue>());
|
||||
if (withFix)
|
||||
{
|
||||
// Post-fix: charlie's balance is unchanged — the withdrawal
|
||||
// was correctly rejected despite being share-denominated.
|
||||
BEAST_EXPECT(charlieBalanceAfter == charlieBalanceBefore);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pre-fix: charlie received the assets, bypassing the
|
||||
// trustline limit.
|
||||
BEAST_EXPECT(charlieBalanceAfter > charlieBalanceBefore);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
@@ -5251,6 +5347,7 @@ public:
|
||||
testVaultClawbackBurnShares();
|
||||
testVaultClawbackAssets();
|
||||
testAssetsMaximum();
|
||||
testBug6_LimitBypassWithShares();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ to_string(ProtocolVersion const& p)
|
||||
}
|
||||
|
||||
std::vector<ProtocolVersion>
|
||||
parseProtocolVersions(boost::beast::string_view const& value)
|
||||
parseProtocolVersions(std::string_view const& value)
|
||||
{
|
||||
static boost::regex const re(
|
||||
"^" // start of line
|
||||
@@ -126,7 +126,7 @@ negotiateProtocolVersion(std::vector<ProtocolVersion> const& versions)
|
||||
}
|
||||
|
||||
std::optional<ProtocolVersion>
|
||||
negotiateProtocolVersion(boost::beast::string_view const& versions)
|
||||
negotiateProtocolVersion(std::string_view const& versions)
|
||||
{
|
||||
auto const them = parseProtocolVersions(versions);
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -39,7 +38,7 @@ to_string(ProtocolVersion const& p);
|
||||
no duplicates and will be sorted in ascending protocol order.
|
||||
*/
|
||||
std::vector<ProtocolVersion>
|
||||
parseProtocolVersions(boost::beast::string_view const& s);
|
||||
parseProtocolVersions(std::string_view const& s);
|
||||
|
||||
/** Given a list of supported protocol versions, choose the one we prefer. */
|
||||
std::optional<ProtocolVersion>
|
||||
@@ -47,7 +46,7 @@ negotiateProtocolVersion(std::vector<ProtocolVersion> const& versions);
|
||||
|
||||
/** Given a list of supported protocol versions, choose the one we prefer. */
|
||||
std::optional<ProtocolVersion>
|
||||
negotiateProtocolVersion(boost::beast::string_view const& versions);
|
||||
negotiateProtocolVersion(std::string_view const& versions);
|
||||
|
||||
/** The list of all the protocol versions we support. */
|
||||
std::string const&
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -229,7 +230,7 @@ ServerHandler::onHandoff(
|
||||
static inline Json::Output
|
||||
makeOutput(Session& session)
|
||||
{
|
||||
return [&](boost::beast::string_view const& b) { session.write(b.data(), b.size()); };
|
||||
return [&](std::string_view const& b) { session.write(b.data(), b.size()); };
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string>
|
||||
@@ -530,11 +531,14 @@ ServerHandler::processSession(
|
||||
makeOutput(*session),
|
||||
coro,
|
||||
forwardedFor(session->request()),
|
||||
[&] {
|
||||
[&]() -> std::string_view {
|
||||
auto const iter = session->request().find("X-User");
|
||||
if (iter != session->request().end())
|
||||
return iter->value();
|
||||
return boost::beast::string_view{};
|
||||
{
|
||||
auto const val = iter->value();
|
||||
return std::string_view(val.data(), val.size());
|
||||
}
|
||||
return std::string_view{};
|
||||
}());
|
||||
|
||||
if (beast::rfc2616::is_keep_alive(session->request()))
|
||||
|
||||
Reference in New Issue
Block a user