mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-05 01:37:00 +00:00
Merge branch 'develop' into ximinez/lending-sendmulti
This commit is contained in:
@@ -102,7 +102,7 @@ ValidPermissionedDomain::finalize(
|
||||
return true;
|
||||
};
|
||||
|
||||
if (view.rules().enabled(fixPermissionedDomainInvariant))
|
||||
if (view.rules().enabled(fixCleanup3_1_3))
|
||||
{
|
||||
// No permissioned domains should be affected if the transaction failed
|
||||
if (!isTesSuccess(result))
|
||||
|
||||
@@ -110,8 +110,8 @@ PermissionedDomainSet::doApply()
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
bool const fix313 = view().rules().enabled(fixCleanup3_1_3);
|
||||
auto const seq = fix313 ? ctx_.tx.getSeqValue() : ctx_.tx.getFieldU32(sfSequence);
|
||||
bool const fixEnabled = view().rules().enabled(fixCleanup3_1_3);
|
||||
auto const seq = fixEnabled ? ctx_.tx.getSeqValue() : ctx_.tx.getFieldU32(sfSequence);
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(account_, seq);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
|
||||
|
||||
@@ -1291,8 +1291,8 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
|
||||
if (numCreds != 0u)
|
||||
{
|
||||
// This array is sorted naturally, but if you willing to change this
|
||||
// behavior don't forget to use credentials::makeSorted
|
||||
// This array is sorted naturally, but if you are going to change
|
||||
// this behavior, don't forget to use credentials::makeSorted
|
||||
STArray credentials(sfAcceptedCredentials, numCreds);
|
||||
for (std::size_t n = 0; n < numCreds; ++n)
|
||||
{
|
||||
@@ -1314,11 +1314,11 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
bool const fixPDEnabled = features[fixPermissionedDomainInvariant];
|
||||
bool const fixEnabled = features[fixCleanup3_1_3];
|
||||
std::initializer_list<TER> const badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED};
|
||||
std::initializer_list<TER> const failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED};
|
||||
|
||||
testcase << "PermissionedDomain" + std::string(fixPDEnabled ? " fix" : "");
|
||||
testcase << "PermissionedDomain" + std::string(fixEnabled ? " fix" : "");
|
||||
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
@@ -1328,7 +1328,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
fixEnabled ? failTers : badTers);
|
||||
|
||||
testcase << "PermissionedDomain 2";
|
||||
|
||||
@@ -1341,7 +1341,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
fixEnabled ? failTers : badTers);
|
||||
|
||||
testcase << "PermissionedDomain 3";
|
||||
doInvariantCheck(
|
||||
@@ -1365,7 +1365,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
fixEnabled ? failTers : badTers);
|
||||
|
||||
testcase << "PermissionedDomain 4";
|
||||
doInvariantCheck(
|
||||
@@ -1388,7 +1388,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
fixEnabled ? failTers : badTers);
|
||||
|
||||
testcase << "PermissionedDomain Set 1";
|
||||
doInvariantCheck(
|
||||
@@ -1409,7 +1409,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
fixEnabled ? failTers : badTers);
|
||||
|
||||
testcase << "PermissionedDomain Set 2";
|
||||
doInvariantCheck(
|
||||
@@ -1440,7 +1440,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
fixEnabled ? failTers : badTers);
|
||||
|
||||
testcase << "PermissionedDomain Set 3";
|
||||
doInvariantCheck(
|
||||
@@ -1470,7 +1470,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
fixEnabled ? failTers : badTers);
|
||||
|
||||
testcase << "PermissionedDomain Set 4";
|
||||
doInvariantCheck(
|
||||
@@ -1498,7 +1498,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
fixEnabled ? failTers : badTers);
|
||||
|
||||
std::initializer_list<TER> const goodTers = {tesSUCCESS, tesSUCCESS};
|
||||
|
||||
@@ -1516,7 +1516,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
testcase << "PermissionedDomain set 2 domains ";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
fixPDEnabled ? badMoreThan1 : emptyV,
|
||||
fixEnabled ? badMoreThan1 : emptyV,
|
||||
[](Account const& a1, Account const& a2, ApplyContext& ac) {
|
||||
createPermissionedDomain(ac, a1, a2);
|
||||
createPermissionedDomain(ac, a1, a2, 2, 11);
|
||||
@@ -1524,7 +1524,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : goodTers);
|
||||
fixEnabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1545,7 +1545,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
std::move(env1),
|
||||
a1,
|
||||
a2,
|
||||
fixPDEnabled ? badMoreThan1 : emptyV,
|
||||
fixEnabled ? badMoreThan1 : emptyV,
|
||||
[&pd1, &pd2](Account const&, Account const&, ApplyContext& ac) {
|
||||
auto sle1 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd1});
|
||||
auto sle2 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd2});
|
||||
@@ -1555,18 +1555,18 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : goodTers);
|
||||
fixEnabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain set 0 domains ";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
fixPDEnabled ? badNoDomains : emptyV,
|
||||
fixEnabled ? badNoDomains : emptyV,
|
||||
[](Account const&, Account const&, ApplyContext&) { return true; },
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? badTers : goodTers);
|
||||
fixEnabled ? badTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1587,11 +1587,11 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
Env(*this, features),
|
||||
a1,
|
||||
a2,
|
||||
fixPDEnabled ? badNoDomains : emptyV,
|
||||
fixEnabled ? badNoDomains : emptyV,
|
||||
[](Account const&, Account const&, ApplyContext&) { return true; },
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}},
|
||||
fixPDEnabled ? badTers : goodTers);
|
||||
fixEnabled ? badTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1611,7 +1611,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
std::move(env1),
|
||||
a1,
|
||||
a2,
|
||||
fixPDEnabled ? badDeleted : emptyV,
|
||||
fixEnabled ? badDeleted : emptyV,
|
||||
[&pd1](Account const&, Account const&, ApplyContext& ac) {
|
||||
auto sle1 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd1});
|
||||
ac.view().erase(sle1);
|
||||
@@ -1619,28 +1619,28 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : goodTers);
|
||||
fixEnabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain del, create domain ";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
fixPDEnabled ? badNotDeleted : emptyV,
|
||||
fixEnabled ? badNotDeleted : emptyV,
|
||||
[](Account const& a1, Account const& a2, ApplyContext& ac) {
|
||||
createPermissionedDomain(ac, a1, a2);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : goodTers);
|
||||
fixEnabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain invalid tx";
|
||||
|
||||
doInvariantCheck(
|
||||
fixPDEnabled ? badTx : emptyV,
|
||||
fixEnabled ? badTx : emptyV,
|
||||
[&](Account const& a1, Account const& a2, ApplyContext& ac) {
|
||||
createPermissionedDomain(ac, a1, a2);
|
||||
return true;
|
||||
@@ -1800,11 +1800,9 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
bool const fixPDEnabled = features[fixPermissionedDomainInvariant];
|
||||
bool const fixS313Enabled = features[fixCleanup3_1_3];
|
||||
bool const fixEnabled = features[fixCleanup3_1_3];
|
||||
|
||||
testcase << "PermissionedDEX" + std::string(fixPDEnabled ? " fixPD" : "") +
|
||||
std::string(fixS313Enabled ? " fixS313" : "");
|
||||
testcase << "PermissionedDEX" + std::string(fixEnabled ? " fix" : "");
|
||||
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
@@ -1908,8 +1906,8 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
std::move(env1),
|
||||
a1,
|
||||
a2,
|
||||
fixS313Enabled ? std::vector<std::string>{{"hybrid offer is malformed"}}
|
||||
: std::vector<std::string>{},
|
||||
fixEnabled ? std::vector<std::string>{{"hybrid offer is malformed"}}
|
||||
: std::vector<std::string>{},
|
||||
[&pd1](Account const& a1, Account const& a2, ApplyContext& ac) {
|
||||
Keylet const offerKey = keylet::offer(a2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
@@ -1926,9 +1924,8 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject&) {}},
|
||||
fixS313Enabled
|
||||
? std::initializer_list<TER>{tecINVARIANT_FAILED, tecINVARIANT_FAILED}
|
||||
: std::initializer_list<TER>{tesSUCCESS, tesSUCCESS});
|
||||
fixEnabled ? std::initializer_list<TER>{tecINVARIANT_FAILED, tecINVARIANT_FAILED}
|
||||
: std::initializer_list<TER>{tesSUCCESS, tesSUCCESS});
|
||||
}
|
||||
|
||||
// hybrid offer missing sfAdditionalBooks
|
||||
@@ -4380,13 +4377,10 @@ public:
|
||||
testNoZeroEscrow();
|
||||
testValidNewAccountRoot();
|
||||
testNFTokenPageInvariants();
|
||||
testPermissionedDomainInvariants(defaultAmendments() | fixPermissionedDomainInvariant);
|
||||
testPermissionedDomainInvariants(defaultAmendments() - fixPermissionedDomainInvariant);
|
||||
testPermissionedDEX(defaultAmendments() | fixPermissionedDomainInvariant);
|
||||
testPermissionedDEX(defaultAmendments() - fixPermissionedDomainInvariant);
|
||||
testPermissionedDEX(
|
||||
(defaultAmendments() | fixPermissionedDomainInvariant) - fixCleanup3_1_3);
|
||||
testPermissionedDEX(defaultAmendments() - fixPermissionedDomainInvariant - fixCleanup3_1_3);
|
||||
testPermissionedDomainInvariants(defaultAmendments() | fixCleanup3_1_3);
|
||||
testPermissionedDomainInvariants(defaultAmendments() - fixCleanup3_1_3);
|
||||
testPermissionedDEX(defaultAmendments() | fixCleanup3_1_3);
|
||||
testPermissionedDEX(defaultAmendments() - fixCleanup3_1_3);
|
||||
testNoModifiedUnmodifiableFields();
|
||||
testValidPseudoAccounts();
|
||||
testValidLoanBroker();
|
||||
|
||||
@@ -1392,10 +1392,10 @@ class PermissionedDEX_test : public beast::unit_test::Suite
|
||||
void
|
||||
testHybridMalformedOffer(FeatureBitset features)
|
||||
{
|
||||
bool const fixS313Enabled = features[fixCleanup3_1_3];
|
||||
bool const fixEnabled = features[fixCleanup3_1_3];
|
||||
|
||||
testcase << "Hybrid offer with empty AdditionalBooks"
|
||||
<< (fixS313Enabled ? " (fixCleanup3_1_3 enabled)" : " (fixCleanup3_1_3 disabled)");
|
||||
<< (fixEnabled ? " (fixCleanup3_1_3 enabled)" : " (fixCleanup3_1_3 disabled)");
|
||||
|
||||
// offerInDomain has two code paths gated by fixCleanup3_1_3:
|
||||
//
|
||||
@@ -1436,7 +1436,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite
|
||||
return true;
|
||||
});
|
||||
|
||||
if (fixS313Enabled)
|
||||
if (fixEnabled)
|
||||
{
|
||||
// post-fixCleanup3_1_3: offerInDomain rejects the malformed
|
||||
// offer (size == 0), so no valid domain offer is found.
|
||||
|
||||
@@ -49,14 +49,10 @@ exceptionExpected(Env& env, json::Value const& jv)
|
||||
|
||||
class PermissionedDomains_test : public beast::unit_test::Suite
|
||||
{
|
||||
FeatureBitset withoutFeature_{testableAmendments() - featurePermissionedDomains};
|
||||
FeatureBitset withFeature_{
|
||||
testableAmendments() //
|
||||
| featurePermissionedDomains | featureCredentials};
|
||||
|
||||
(testableAmendments() | featurePermissionedDomains | featureCredentials) - fixCleanup3_1_3};
|
||||
FeatureBitset withFix_{
|
||||
testableAmendments() //
|
||||
| featurePermissionedDomains | featureCredentials};
|
||||
testableAmendments() | featurePermissionedDomains | featureCredentials | fixCleanup3_1_3};
|
||||
|
||||
// Verify that each tx type can execute if the feature is enabled.
|
||||
void
|
||||
@@ -98,7 +94,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite
|
||||
{
|
||||
testcase("Disabled");
|
||||
Account const alice("alice");
|
||||
Env env(*this, withoutFeature_);
|
||||
Env env(*this, testableAmendments() - featurePermissionedDomains);
|
||||
env.fund(XRP(1000), alice);
|
||||
pdomain::Credentials const credentials{{alice, "first credential"}};
|
||||
env(pdomain::setTx(alice, credentials), Ter(temDISABLED));
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
@@ -252,32 +253,46 @@ forwardedFor(http_request_type const& request)
|
||||
// Look for the Forwarded field in the request.
|
||||
if (auto it = request.find(boost::beast::http::field::forwarded); it != request.end())
|
||||
{
|
||||
auto asciiTolower = [](char c) -> char {
|
||||
auto asciiToLower = [](char c) -> char {
|
||||
return ((static_cast<unsigned>(c) - 65U) < 26) ? c + 'a' - 'A' : c;
|
||||
};
|
||||
|
||||
// Look for the first (case insensitive) "for="
|
||||
static std::string const kFOR_STR{"for="};
|
||||
char const* found = std::search(
|
||||
it->value().begin(),
|
||||
it->value().end(),
|
||||
kFOR_STR.begin(),
|
||||
kFOR_STR.end(),
|
||||
[&asciiTolower](char c1, char c2) { return asciiTolower(c1) == asciiTolower(c2); });
|
||||
// Look for the first (case insensitive) "for=" at a directive
|
||||
// boundary (start of value, or preceded by , ; or OWS).
|
||||
static constexpr std::string_view kFOR_STR{"for="};
|
||||
auto const atFieldBoundary = [begin = it->value().begin()](auto p) {
|
||||
return p == begin || p[-1] == ';' || p[-1] == ',' || p[-1] == ' ' || p[-1] == '\t';
|
||||
};
|
||||
auto found = it->value().begin();
|
||||
while (true)
|
||||
{
|
||||
found = std::search(
|
||||
found,
|
||||
it->value().end(),
|
||||
kFOR_STR.begin(),
|
||||
kFOR_STR.end(),
|
||||
[&asciiToLower](char c1, char c2) { return asciiToLower(c1) == asciiToLower(c2); });
|
||||
|
||||
if (found == it->value().end())
|
||||
return {};
|
||||
if (found == it->value().end())
|
||||
return {};
|
||||
|
||||
found += kFOR_STR.size();
|
||||
if (atFieldBoundary(found))
|
||||
break;
|
||||
|
||||
++found;
|
||||
}
|
||||
|
||||
std::advance(found, kFOR_STR.size());
|
||||
|
||||
// We found a "for=". Scan for the end of the IP address.
|
||||
std::size_t const pos = [&found, &it]() {
|
||||
auto const remaining = static_cast<std::size_t>(it->value().end() - found);
|
||||
if (std::size_t const pos = std::string_view(found, remaining).find_first_of(",;");
|
||||
pos != std::string_view::npos)
|
||||
auto const end = it->value().end();
|
||||
std::size_t const pos = [&found, &end]() {
|
||||
std::size_t const pos =
|
||||
std::string_view(found, std::distance(found, end)).find_first_of(",;");
|
||||
if (pos != std::string_view::npos)
|
||||
return pos;
|
||||
|
||||
return remaining;
|
||||
return static_cast<std::size_t>(std::distance(found, end));
|
||||
}();
|
||||
|
||||
return extractIpAddrFromField({found, pos});
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
@@ -54,6 +55,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -405,6 +407,25 @@ checkTxJsonFields(
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Expected<void, json::Value>
|
||||
checkNetworkID(json::Value const& txJson, uint32_t appNetworkId)
|
||||
{
|
||||
if (appNetworkId > 1024)
|
||||
{
|
||||
if (!txJson.isMember(jss::NetworkID))
|
||||
{
|
||||
return Unexpected(
|
||||
RPC::makeError(RpcInvalidParams, RPC::missingFieldMessage("tx_json.NetworkID")));
|
||||
}
|
||||
if (!txJson[jss::NetworkID].isIntegral() || txJson[jss::NetworkID].asUInt() != appNetworkId)
|
||||
{
|
||||
return Unexpected(
|
||||
RPC::makeError(RpcInvalidParams, RPC::invalidFieldMessage("tx_json.NetworkID")));
|
||||
}
|
||||
}
|
||||
return Expected<void, json::Value>();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// A move-only struct that makes it easy to return either a json::Value or a
|
||||
@@ -1165,8 +1186,16 @@ transactionSignFor(
|
||||
if (!txJson.isObject())
|
||||
return RPC::objectFieldError(jss::tx_json);
|
||||
|
||||
// If the tx_json.SigningPubKey field is missing,
|
||||
// insert an empty one.
|
||||
if (auto checkResult =
|
||||
detail::checkNetworkID(txJson, app.getNetworkIDService().getNetworkID());
|
||||
!checkResult)
|
||||
{
|
||||
return std::move(checkResult).error();
|
||||
}
|
||||
|
||||
// If the tx_json.SigningPubKey field is missing, insert an empty one,
|
||||
// in order for the `checkMultiSignFields` to not return an error
|
||||
// for non-multisign transactions.
|
||||
if (!txJson.isMember(sfSigningPubKey.getJsonName()))
|
||||
txJson[sfSigningPubKey.getJsonName()] = "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user