mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-02 08:17:13 +00:00
fix: Make assorted RPC fixes (#6529)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -40,6 +40,14 @@ This section contains changes targeting a future version.
|
||||
- Peer Crawler: The `port` field in `overlay.active[]` now consistently returns an integer instead of a string for outbound peers. [#6318](https://github.com/XRPLF/rippled/pull/6318)
|
||||
- `ping`: The `ip` field is no longer returned as an empty string for proxied connections without a forwarded-for header. It is now omitted, consistent with the behavior for identified connections. [#6730](https://github.com/XRPLF/rippled/pull/6730)
|
||||
- gRPC `GetLedgerDiff`: Fixed error message that incorrectly said "base ledger not validated" when the desired ledger was not validated. [#6730](https://github.com/XRPLF/rippled/pull/6730)
|
||||
- `account_channels`: The `destination_account` field now returns an error if the value is not a string. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- `subscribe`: The `taker` field in the `books` array now returns an error if the value is not a string. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- `account_info`: The `urlgravatar` field now uses HTTPS instead of HTTP. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- `ledger`: The `full`, `accounts`, `transactions`, `expand`, `binary`, `owner_funds`, and `queue` fields now return an error if the value is not a boolean. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- `ledger_data`: The `binary` field now returns an error if the value is not a boolean. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- `submit`: The `fail_hard` field now returns an error if the value is not a boolean. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- `subscribe`: The `taker` field in the `books` array now returns `actMalformed` instead of `badIssuer` if the value is not a valid account. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- Fixed a bug in `Forwarded` HTTP header parsing where the extracted IP address could be incorrect when no comma or semicolon delimiter follows the address. This could cause the server to misidentify a client's IP address when operating behind a reverse proxy. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
|
||||
## XRP Ledger server version 3.1.0
|
||||
|
||||
|
||||
@@ -1019,7 +1019,8 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
auto testInvalidAccountParam = [&](auto const& param) {
|
||||
Json::Value params;
|
||||
params[jss::account] = param;
|
||||
auto jrr = env.rpc("json", "account_channels", to_string(params))[jss::result];
|
||||
auto const jrr =
|
||||
env.rpc("json", "account_channels", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'account'.");
|
||||
};
|
||||
@@ -1031,6 +1032,25 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testInvalidAccountParam(Json::Value(Json::objectValue));
|
||||
testInvalidAccountParam(Json::Value(Json::arrayValue));
|
||||
}
|
||||
{
|
||||
// test destination_account non-string
|
||||
auto testInvalidDestAccountParam = [&](auto const& param) {
|
||||
Json::Value params;
|
||||
params[jss::account] = alice.human();
|
||||
params[jss::destination_account] = param;
|
||||
auto const jrr =
|
||||
env.rpc("json", "account_channels", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'destination_account'.");
|
||||
};
|
||||
|
||||
testInvalidDestAccountParam(1);
|
||||
testInvalidDestAccountParam(1.1);
|
||||
testInvalidDestAccountParam(true);
|
||||
testInvalidDestAccountParam(Json::Value(Json::nullValue));
|
||||
testInvalidDestAccountParam(Json::Value(Json::objectValue));
|
||||
testInvalidDestAccountParam(Json::Value(Json::arrayValue));
|
||||
}
|
||||
{
|
||||
auto const r = env.rpc("account_channels", alice.human(), bob.human());
|
||||
BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
|
||||
|
||||
@@ -165,6 +165,16 @@ public:
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
|
||||
{
|
||||
// binary not a boolean
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::binary] = "true";
|
||||
auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'binary', not boolean.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -165,6 +165,23 @@ class LedgerRPC_test : public beast::unit_test::suite
|
||||
auto const ret = env.rpc("json", "ledger", "{ \"ledger_index\" : 1000000000000000 }");
|
||||
checkErrorValue(ret, "invalidParams", "Invalid parameters.");
|
||||
}
|
||||
|
||||
{
|
||||
// test all boolean fields with non-boolean values
|
||||
auto testBooleanField = [&](Json::StaticString const& field) {
|
||||
Json::Value jvParams;
|
||||
jvParams[field] = "blah";
|
||||
auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "invalidParams", "Invalid parameters.");
|
||||
};
|
||||
testBooleanField(jss::full);
|
||||
testBooleanField(jss::accounts);
|
||||
testBooleanField(jss::transactions);
|
||||
testBooleanField(jss::expand);
|
||||
testBooleanField(jss::binary);
|
||||
testBooleanField(jss::owner_funds);
|
||||
testBooleanField(jss::queue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -25,6 +25,7 @@ class Roles_test : public beast::unit_test::suite
|
||||
void
|
||||
testRoles()
|
||||
{
|
||||
testcase("Roles");
|
||||
using namespace test::jtx;
|
||||
|
||||
{
|
||||
@@ -224,6 +225,21 @@ class Roles_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(rpcRes["role"] == "proxied");
|
||||
BEAST_EXPECT(rpcRes["ip"] == "::11:22:33:44:45.55.65.75");
|
||||
BEAST_EXPECT(isValidIpAddress(rpcRes["ip"].asString()));
|
||||
|
||||
// Test: "for=" not at the beginning AND no trailing delimiter.
|
||||
// This exercises the fix for the buffer over-read bug where
|
||||
// the remaining length was incorrectly calculated.
|
||||
headers["Forwarded"] = "by=203.0.113.43;for=1.2.3.4";
|
||||
rpcRes = env.rpc(headers, "ping")["result"];
|
||||
BEAST_EXPECT(rpcRes["role"] == "proxied");
|
||||
BEAST_EXPECT(rpcRes["ip"] == "1.2.3.4");
|
||||
BEAST_EXPECT(isValidIpAddress(rpcRes["ip"].asString()));
|
||||
|
||||
headers["Forwarded"] = "proto=https;by=proxy.example.com;for=5.6.7.8";
|
||||
rpcRes = env.rpc(headers, "ping")["result"];
|
||||
BEAST_EXPECT(rpcRes["role"] == "proxied");
|
||||
BEAST_EXPECT(rpcRes["ip"] == "5.6.7.8");
|
||||
BEAST_EXPECT(isValidIpAddress(rpcRes["ip"].asString()));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -252,6 +268,7 @@ class Roles_test : public beast::unit_test::suite
|
||||
testInvalidIpAddresses()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
testcase("Invalid IP addresses");
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
|
||||
98
src/test/rpc/Submit_test.cpp
Normal file
98
src/test/rpc/Submit_test.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include <test/jtx/Account.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/JTx.h>
|
||||
#include <test/jtx/amount.h>
|
||||
#include <test/jtx/pay.h>
|
||||
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/Seed.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace xrpl::test {
|
||||
|
||||
class Submit_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testFailHardValidation()
|
||||
{
|
||||
testcase("fail_hard parameter validation");
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env.close();
|
||||
|
||||
// Lambda to test invalid fail_hard parameter types
|
||||
auto testInvalidFailHard = [&](auto const& param) {
|
||||
// Test with tx_blob path
|
||||
{
|
||||
JTx const jt = env.jt(pay(alice, bob, XRP(1)));
|
||||
auto const txBlob = strHex(jt.stx->getSerializer().slice());
|
||||
|
||||
Json::Value params;
|
||||
params[jss::tx_blob] = txBlob;
|
||||
params[jss::fail_hard] = param;
|
||||
auto const jrr = env.rpc("json", "submit", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'fail_hard', not boolean.");
|
||||
}
|
||||
|
||||
// Test with tx_json path (deprecated signing)
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::secret] = toBase58(generateSeed("alice"));
|
||||
params[jss::tx_json] = pay("alice", "bob", XRP(1));
|
||||
params[jss::fail_hard] = param;
|
||||
auto const jrr = env.rpc("json", "submit", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'fail_hard', not boolean.");
|
||||
}
|
||||
};
|
||||
|
||||
// Test all invalid types
|
||||
testInvalidFailHard("true");
|
||||
testInvalidFailHard("yes");
|
||||
testInvalidFailHard(1);
|
||||
testInvalidFailHard(0);
|
||||
testInvalidFailHard(1.5);
|
||||
testInvalidFailHard(Json::Value(Json::objectValue));
|
||||
testInvalidFailHard(Json::Value(Json::arrayValue));
|
||||
|
||||
// Valid boolean values should work (not return invalidParams)
|
||||
{
|
||||
JTx const jt = env.jt(pay(alice, bob, XRP(1)));
|
||||
auto const txBlob = strHex(jt.stx->getSerializer().slice());
|
||||
|
||||
Json::Value params;
|
||||
params[jss::tx_blob] = txBlob;
|
||||
params[jss::fail_hard] = true;
|
||||
auto const jrr = env.rpc("json", "submit", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(!jrr.isMember(jss::error) || jrr[jss::error] != "invalidParams");
|
||||
}
|
||||
{
|
||||
JTx const jt = env.jt(pay(alice, bob, XRP(1)));
|
||||
auto const txBlob = strHex(jt.stx->getSerializer().slice());
|
||||
|
||||
Json::Value params;
|
||||
params[jss::tx_blob] = txBlob;
|
||||
params[jss::fail_hard] = false;
|
||||
auto const jrr = env.rpc("json", "submit", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(!jrr.isMember(jss::error) || jrr[jss::error] != "invalidParams");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testFailHardValidation();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Submit, rpc, xrpl);
|
||||
|
||||
} // namespace xrpl::test
|
||||
@@ -214,16 +214,12 @@ public:
|
||||
// Check stream update for payment transaction
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]["NewFields"]
|
||||
[jss::Account] //
|
||||
== Account("alice").human() &&
|
||||
jv[jss::transaction][jss::TransactionType] //
|
||||
== jss::Payment &&
|
||||
jv[jss::transaction][jss::DeliverMax] //
|
||||
== std::to_string(10000000000 + baseFee) &&
|
||||
jv[jss::transaction][jss::Fee] //
|
||||
== std::to_string(baseFee) &&
|
||||
jv[jss::transaction][jss::Sequence] //
|
||||
== 1;
|
||||
[jss::Account] == Account("alice").human() &&
|
||||
jv[jss::transaction][jss::TransactionType] == jss::Payment &&
|
||||
jv[jss::transaction][jss::DeliverMax] ==
|
||||
std::to_string(10000000000 + baseFee) &&
|
||||
jv[jss::transaction][jss::Fee] == std::to_string(baseFee) &&
|
||||
jv[jss::transaction][jss::Sequence] == 1;
|
||||
}));
|
||||
|
||||
// Check stream update for accountset transaction
|
||||
@@ -579,7 +575,7 @@ public:
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
auto jr = env.rpc("json", method, "{}")[jss::result];
|
||||
auto const jr = env.rpc("json", method, "{}")[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
@@ -589,7 +585,7 @@ public:
|
||||
jv[jss::url] = "not-a-url";
|
||||
jv[jss::username] = "admin";
|
||||
jv[jss::password] = "password";
|
||||
auto jr = env.rpc("json", method, to_string(jv))[jss::result];
|
||||
auto const jr = env.rpc("json", method, to_string(jv))[jss::result];
|
||||
if (subscribe)
|
||||
{
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
@@ -602,7 +598,7 @@ public:
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::url] = "ftp://scheme.not.supported.tld";
|
||||
auto jr = env.rpc("json", method, to_string(jv))[jss::result];
|
||||
auto const jr = env.rpc("json", method, to_string(jv))[jss::result];
|
||||
if (subscribe)
|
||||
{
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
@@ -614,7 +610,7 @@ public:
|
||||
Env env_nonadmin{*this, single_thread_io(no_admin(envconfig()))};
|
||||
Json::Value jv;
|
||||
jv[jss::url] = "no-url";
|
||||
auto jr = env_nonadmin.rpc("json", method, to_string(jv))[jss::result];
|
||||
auto const jr = env_nonadmin.rpc("json", method, to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "noPermission");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "You don't have permission for this command.");
|
||||
}
|
||||
@@ -634,7 +630,7 @@ public:
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[f] = nonArray;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
@@ -642,7 +638,7 @@ public:
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[f] = Json::arrayValue;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
|
||||
}
|
||||
@@ -652,7 +648,7 @@ public:
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::books] = nonArray;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
@@ -661,7 +657,7 @@ public:
|
||||
Json::Value jv;
|
||||
jv[jss::books] = Json::arrayValue;
|
||||
jv[jss::books][0u] = 1;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
@@ -672,7 +668,7 @@ public:
|
||||
jv[jss::books][0u] = Json::objectValue;
|
||||
jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
|
||||
jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "srcCurMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Source currency is malformed.");
|
||||
}
|
||||
@@ -684,7 +680,7 @@ public:
|
||||
jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
|
||||
jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
|
||||
jv[jss::books][0u][jss::taker_pays][jss::currency] = "ZZZZ";
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "srcCurMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Source currency is malformed.");
|
||||
}
|
||||
@@ -697,7 +693,7 @@ public:
|
||||
jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
|
||||
jv[jss::books][0u][jss::taker_pays][jss::currency] = "USD";
|
||||
jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "srcIsrMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Source issuer is malformed.");
|
||||
}
|
||||
@@ -710,7 +706,7 @@ public:
|
||||
jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
|
||||
jv[jss::books][0u][jss::taker_pays][jss::currency] = "USD";
|
||||
jv[jss::books][0u][jss::taker_pays][jss::issuer] = Account{"gateway"}.human() + "DEAD";
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "srcIsrMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Source issuer is malformed.");
|
||||
}
|
||||
@@ -722,7 +718,7 @@ public:
|
||||
jv[jss::books][0u][jss::taker_pays] =
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::include_date);
|
||||
jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
// NOTE: this error is slightly incongruous with the
|
||||
// equivalent source currency error
|
||||
BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed");
|
||||
@@ -737,7 +733,7 @@ public:
|
||||
jv[jss::books][0u][jss::taker_pays] =
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::include_date);
|
||||
jv[jss::books][0u][jss::taker_gets][jss::currency] = "ZZZZ";
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
// NOTE: this error is slightly incongruous with the
|
||||
// equivalent source currency error
|
||||
BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed");
|
||||
@@ -753,7 +749,7 @@ public:
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::include_date);
|
||||
jv[jss::books][0u][jss::taker_gets][jss::currency] = "USD";
|
||||
jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "dstIsrMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Destination issuer is malformed.");
|
||||
}
|
||||
@@ -766,7 +762,7 @@ public:
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::include_date);
|
||||
jv[jss::books][0u][jss::taker_gets][jss::currency] = "USD";
|
||||
jv[jss::books][0u][jss::taker_gets][jss::issuer] = Account{"gateway"}.human() + "DEAD";
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "dstIsrMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Destination issuer is malformed.");
|
||||
}
|
||||
@@ -779,7 +775,7 @@ public:
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::include_date);
|
||||
jv[jss::books][0u][jss::taker_gets] =
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::include_date);
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "badMarket");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "No such market.");
|
||||
}
|
||||
@@ -788,7 +784,7 @@ public:
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::streams] = nonArray;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
@@ -797,7 +793,7 @@ public:
|
||||
Json::Value jv;
|
||||
jv[jss::streams] = Json::arrayValue;
|
||||
jv[jss::streams][0u] = 1;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "malformedStream");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Stream malformed.");
|
||||
}
|
||||
@@ -806,10 +802,55 @@ public:
|
||||
Json::Value jv;
|
||||
jv[jss::streams] = Json::arrayValue;
|
||||
jv[jss::streams][0u] = "not_a_stream";
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "malformedStream");
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Stream malformed.");
|
||||
}
|
||||
|
||||
if (subscribe)
|
||||
{
|
||||
// invalid taker - not a string
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::books] = Json::arrayValue;
|
||||
jv[jss::books][0u] = Json::objectValue;
|
||||
jv[jss::books][0u][jss::taker_pays] =
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::include_date);
|
||||
jv[jss::books][0u][jss::taker_gets][jss::currency] = "XRP";
|
||||
jv[jss::books][0u][jss::taker] = 1;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECTS(jr[jss::error] == "actMalformed", jr.toStyledString());
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
|
||||
}
|
||||
|
||||
// invalid taker - malformed account string
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::books] = Json::arrayValue;
|
||||
jv[jss::books][0u] = Json::objectValue;
|
||||
jv[jss::books][0u][jss::taker_pays] =
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::include_date);
|
||||
jv[jss::books][0u][jss::taker_gets][jss::currency] = "XRP";
|
||||
jv[jss::books][0u][jss::taker] = "not_an_account";
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECTS(jr[jss::error] == "actMalformed", jr.toStyledString());
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
|
||||
}
|
||||
|
||||
// invalid taker - account string with extra characters
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::books] = Json::arrayValue;
|
||||
jv[jss::books][0u] = Json::objectValue;
|
||||
jv[jss::books][0u][jss::taker_pays] =
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::include_date);
|
||||
jv[jss::books][0u][jss::taker_gets][jss::currency] = "XRP";
|
||||
jv[jss::books][0u][jss::taker] = Account{"alice"}.human() + "DEAD";
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECTS(jr[jss::error] == "actMalformed", jr.toStyledString());
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -272,12 +272,12 @@ forwardedFor(http_request_type const& request)
|
||||
|
||||
// We found a "for=". Scan for the end of the IP address.
|
||||
std::size_t const pos = [&found, &it]() {
|
||||
std::size_t const pos =
|
||||
std::string_view(found, it->value().end() - found).find_first_of(",;");
|
||||
if (pos != std::string_view::npos)
|
||||
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)
|
||||
return pos;
|
||||
|
||||
return it->value().size() - forStr.size();
|
||||
return remaining;
|
||||
}();
|
||||
|
||||
return extractIpAddrFromField({found, pos});
|
||||
|
||||
@@ -575,11 +575,7 @@ transactionPreProcessImpl(
|
||||
|
||||
if (verify)
|
||||
{
|
||||
if (!sle)
|
||||
{
|
||||
// XXX Ignore transactions for accounts not created.
|
||||
return rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
// sle validity is checked above
|
||||
|
||||
JLOG(j.trace()) << "verify: " << toBase58(calcAccountID(pk)) << " : "
|
||||
<< toBase58(srcAddressID);
|
||||
|
||||
@@ -95,7 +95,11 @@ doAccountChannels(RPC::JsonContext& context)
|
||||
|
||||
std::string strDst;
|
||||
if (params.isMember(jss::destination_account))
|
||||
{
|
||||
if (!params[jss::destination_account].isString())
|
||||
return RPC::invalid_field_error(jss::destination_account);
|
||||
strDst = params[jss::destination_account].asString();
|
||||
}
|
||||
|
||||
auto const raDstAccount = [&]() -> std::optional<AccountID> {
|
||||
return strDst.empty() ? std::nullopt : parseBase58<AccountID>(strDst);
|
||||
|
||||
@@ -58,10 +58,9 @@ injectSLE(Json::Value& jv, SLE const& sle)
|
||||
Blob const b(hash.begin(), hash.end());
|
||||
std::string md5 = strHex(makeSlice(b));
|
||||
boost::to_lower(md5);
|
||||
// VFALCO TODO Give a name and move this constant
|
||||
// to a more visible location. Also
|
||||
// shouldn't this be https?
|
||||
jv[jss::urlgravatar] = str(boost::format("http://www.gravatar.com/avatar/%s") % md5);
|
||||
// VFALCO TODO Give a name to this constant and move it
|
||||
// to a more visible location.
|
||||
jv[jss::urlgravatar] = str(boost::format("https://www.gravatar.com/avatar/%s") % md5);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -118,14 +118,14 @@ doNoRippleCheck(RPC::JsonContext& context)
|
||||
|
||||
bool const bDefaultRipple = (sle->getFieldU32(sfFlags) & lsfDefaultRipple) != 0u;
|
||||
|
||||
if ((static_cast<int>(bDefaultRipple) & static_cast<int>(!roleGateway)) != 0)
|
||||
if (bDefaultRipple && !roleGateway)
|
||||
{
|
||||
problems.append(
|
||||
"You appear to have set your default ripple flag even though you "
|
||||
"are not a gateway. This is not recommended unless you are "
|
||||
"experimenting");
|
||||
}
|
||||
else if ((static_cast<int>(roleGateway) & static_cast<int>(!bDefaultRipple)) != 0)
|
||||
else if (roleGateway && !bDefaultRipple)
|
||||
{
|
||||
problems.append("You should immediately set your default ripple flag");
|
||||
if (transactions)
|
||||
@@ -148,12 +148,12 @@ doNoRippleCheck(RPC::JsonContext& context)
|
||||
|
||||
std::string problem;
|
||||
bool needFix = false;
|
||||
if (bNoRipple & roleGateway)
|
||||
if (bNoRipple && roleGateway)
|
||||
{
|
||||
problem = "You should clear the no ripple flag on your ";
|
||||
needFix = true;
|
||||
}
|
||||
else if (!roleGateway & !bNoRipple)
|
||||
else if (!roleGateway && !bNoRipple)
|
||||
{
|
||||
problem = "You should probably set the no ripple flag on your ";
|
||||
needFix = true;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
@@ -42,28 +43,56 @@ Status
|
||||
LedgerHandler::check()
|
||||
{
|
||||
auto const& params = context_.params;
|
||||
|
||||
auto getBool = [&](Json::StaticString const& field) -> Expected<bool, Status> {
|
||||
if (!params.isMember(field))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!params[field].isBool())
|
||||
{
|
||||
return Unexpected(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
return params[field].asBool();
|
||||
};
|
||||
|
||||
auto const full = getBool(jss::full);
|
||||
auto const transactions = getBool(jss::transactions);
|
||||
auto const accounts = getBool(jss::accounts);
|
||||
auto const expand = getBool(jss::expand);
|
||||
auto const binary = getBool(jss::binary);
|
||||
auto const owner_funds = getBool(jss::owner_funds);
|
||||
auto const queue = getBool(jss::queue);
|
||||
|
||||
if (!full.has_value())
|
||||
return full.error();
|
||||
if (!transactions.has_value())
|
||||
return transactions.error();
|
||||
if (!accounts.has_value())
|
||||
return accounts.error();
|
||||
if (!expand.has_value())
|
||||
return expand.error();
|
||||
if (!binary.has_value())
|
||||
return binary.error();
|
||||
if (!owner_funds.has_value())
|
||||
return owner_funds.error();
|
||||
if (!queue.has_value())
|
||||
return queue.error();
|
||||
|
||||
options_ = (*full ? LedgerFill::full : 0) | (*expand ? LedgerFill::expand : 0) |
|
||||
(*transactions ? LedgerFill::dumpTxrp : 0) | (*accounts ? LedgerFill::dumpState : 0) |
|
||||
(*binary ? LedgerFill::binary : 0) | (*owner_funds ? LedgerFill::ownerFunds : 0) |
|
||||
(*queue ? LedgerFill::dumpQueue : 0);
|
||||
|
||||
bool const needsLedger = params.isMember(jss::ledger) || params.isMember(jss::ledger_hash) ||
|
||||
params.isMember(jss::ledger_index);
|
||||
if (!needsLedger)
|
||||
return Status::OK;
|
||||
|
||||
if (auto s = lookupLedger(ledger_, context_, result_))
|
||||
return s;
|
||||
|
||||
bool const full = params[jss::full].asBool();
|
||||
bool const transactions = params[jss::transactions].asBool();
|
||||
bool const accounts = params[jss::accounts].asBool();
|
||||
bool const expand = params[jss::expand].asBool();
|
||||
bool const binary = params[jss::binary].asBool();
|
||||
bool const owner_funds = params[jss::owner_funds].asBool();
|
||||
bool const queue = params[jss::queue].asBool();
|
||||
|
||||
options_ = (full ? LedgerFill::full : 0) | (expand ? LedgerFill::expand : 0) |
|
||||
(transactions ? LedgerFill::dumpTxrp : 0) | (accounts ? LedgerFill::dumpState : 0) |
|
||||
(binary ? LedgerFill::binary : 0) | (owner_funds ? LedgerFill::ownerFunds : 0) |
|
||||
(queue ? LedgerFill::dumpQueue : 0);
|
||||
|
||||
if (full || accounts)
|
||||
if (*full || *accounts)
|
||||
{
|
||||
// Until some sane way to get full ledgers has been implemented,
|
||||
// disallow retrieving all state nodes.
|
||||
@@ -76,7 +105,8 @@ LedgerHandler::check()
|
||||
}
|
||||
context_.loadType = binary ? Resource::feeMediumBurdenRPC : Resource::feeHeavyBurdenRPC;
|
||||
}
|
||||
if (queue)
|
||||
|
||||
if (*queue)
|
||||
{
|
||||
if (!ledger_ || !ledger_->open())
|
||||
{
|
||||
|
||||
@@ -54,7 +54,13 @@ doLedgerData(RPC::JsonContext& context)
|
||||
return RPC::expected_field_error(jss::marker, "valid");
|
||||
}
|
||||
|
||||
bool const isBinary = params[jss::binary].asBool();
|
||||
bool isBinary = false;
|
||||
if (params.isMember(jss::binary))
|
||||
{
|
||||
if (!params[jss::binary].isBool())
|
||||
return RPC::expected_field_error(jss::binary, "boolean");
|
||||
isBinary = params[jss::binary].asBool();
|
||||
}
|
||||
|
||||
int limit = -1;
|
||||
if (params.isMember(jss::limit))
|
||||
|
||||
@@ -241,7 +241,6 @@ doGetAggregatePrice(RPC::JsonContext& context)
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get the ledger
|
||||
std::shared_ptr<ReadView const> ledger;
|
||||
result = RPC::lookupLedger(ledger, context);
|
||||
if (!ledger)
|
||||
|
||||
@@ -248,9 +248,11 @@ doSubscribe(RPC::JsonContext& context)
|
||||
|
||||
if (j.isMember(jss::taker))
|
||||
{
|
||||
if (!j[jss::taker].isString())
|
||||
return rpcError(rpcACT_MALFORMED);
|
||||
takerID = parseBase58<AccountID>(j[jss::taker].asString());
|
||||
if (!takerID)
|
||||
return rpcError(rpcBAD_ISSUER);
|
||||
return rpcError(rpcACT_MALFORMED);
|
||||
}
|
||||
|
||||
if (j.isMember(jss::domain))
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/detail/TransactionSign.h>
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/RPCErr.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
@@ -24,11 +26,15 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
static NetworkOPs::FailHard
|
||||
static Expected<NetworkOPs::FailHard, Json::Value>
|
||||
getFailHard(RPC::JsonContext const& context)
|
||||
{
|
||||
if (context.params.isMember(jss::fail_hard) && !context.params[jss::fail_hard].isBool())
|
||||
{
|
||||
return Unexpected(RPC::expected_field_error(jss::fail_hard, "boolean"));
|
||||
}
|
||||
return NetworkOPs::doFailHard(
|
||||
context.params.isMember("fail_hard") && context.params["fail_hard"].asBool());
|
||||
context.params.isMember(jss::fail_hard) && context.params[jss::fail_hard].asBool());
|
||||
}
|
||||
|
||||
// {
|
||||
@@ -43,6 +49,8 @@ doSubmit(RPC::JsonContext& context)
|
||||
if (!context.params.isMember(jss::tx_blob))
|
||||
{
|
||||
auto const failType = getFailHard(context);
|
||||
if (!failType)
|
||||
return failType.error();
|
||||
|
||||
if (context.role != Role::ADMIN && !context.app.config().canSign())
|
||||
return RPC::make_error(rpcNOT_SUPPORTED, "Signing is not supported by this server.");
|
||||
@@ -50,7 +58,7 @@ doSubmit(RPC::JsonContext& context)
|
||||
auto ret = RPC::transactionSubmit(
|
||||
context.params,
|
||||
context.apiVersion,
|
||||
failType,
|
||||
*failType,
|
||||
context.role,
|
||||
context.ledgerMaster.getValidatedLedgerAge(),
|
||||
context.app,
|
||||
@@ -118,8 +126,10 @@ doSubmit(RPC::JsonContext& context)
|
||||
try
|
||||
{
|
||||
auto const failType = getFailHard(context);
|
||||
if (!failType)
|
||||
return failType.error();
|
||||
|
||||
context.netOps.processTransaction(transaction, isUnlimited(context.role), true, failType);
|
||||
context.netOps.processTransaction(transaction, isUnlimited(context.role), true, *failType);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user